You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

client.go 2.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. // Package ident implements an RFC 1413 client
  2. package ident
  3. import (
  4. "bufio"
  5. "fmt"
  6. "net"
  7. "strings"
  8. "time"
  9. )
  10. // Response is a successful answer to our query to the identd server.
  11. type Response struct {
  12. OS string
  13. Charset string
  14. Identifier string
  15. }
  16. // ResponseError indicates that the identd server returned an error rather than an
  17. // identifying string.
  18. type ResponseError struct {
  19. Type string
  20. }
  21. func (e ResponseError) Error() string {
  22. return fmt.Sprintf("Ident error: %s", e.Type)
  23. }
  24. // ProtocolError indicates that an error occurred with the protocol itself, that the response
  25. // could not be successfully parsed or was malformed.
  26. type ProtocolError struct {
  27. Line string
  28. }
  29. func (e ProtocolError) Error() string {
  30. return fmt.Sprintf("Unexpected response from server: %s", e.Line)
  31. }
  32. // Query makes an Ident query, if timeout is >0 the query is timed out after that many seconds.
  33. func Query(ip string, portOnServer, portOnClient int, timeout time.Duration) (response Response, err error) {
  34. var conn net.Conn
  35. if timeout > 0 {
  36. conn, err = net.DialTimeout("tcp", net.JoinHostPort(ip, "113"), timeout)
  37. } else {
  38. conn, err = net.Dial("tcp", net.JoinHostPort(ip, "113"))
  39. }
  40. if err != nil {
  41. return
  42. }
  43. // stop the ident read after <timeout> seconds
  44. if timeout > 0 {
  45. conn.SetDeadline(time.Now().Add(timeout))
  46. }
  47. _, err = conn.Write([]byte(fmt.Sprintf("%d, %d", portOnClient, portOnServer) + "\r\n"))
  48. if err != nil {
  49. return
  50. }
  51. r := bufio.NewReaderSize(conn, 1024)
  52. respBytes, err := r.ReadSlice('\n')
  53. if err != nil {
  54. return
  55. }
  56. resp := string(respBytes)
  57. fields := strings.SplitN(resp, ":", 4)
  58. if len(fields) < 3 {
  59. return response, ProtocolError{resp}
  60. }
  61. for i, field := range fields {
  62. fields[i] = strings.TrimSpace(field)
  63. }
  64. switch fields[1] {
  65. case "USERID":
  66. if len(fields) != 4 {
  67. return response, ProtocolError{resp}
  68. }
  69. var os, charset string
  70. osAndCharset := strings.SplitN(fields[2], ",", 2)
  71. if len(osAndCharset) == 2 {
  72. os = osAndCharset[0]
  73. charset = osAndCharset[1]
  74. } else {
  75. os = osAndCharset[0]
  76. charset = "US-ASCII"
  77. }
  78. return Response{
  79. OS: os,
  80. Charset: charset,
  81. Identifier: fields[3],
  82. }, nil
  83. case "ERROR":
  84. if len(fields) != 3 {
  85. return response, ProtocolError{resp}
  86. }
  87. return response, ResponseError{fields[2]}
  88. default:
  89. err = ProtocolError{resp}
  90. }
  91. return
  92. }