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.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  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. // if a timeout is set, respect it from the beginning of the query, including the dial time
  35. var deadline time.Time
  36. if timeout > 0 {
  37. deadline = time.Now().Add(timeout)
  38. }
  39. var conn net.Conn
  40. if timeout > 0 {
  41. conn, err = net.DialTimeout("tcp", net.JoinHostPort(ip, "113"), timeout)
  42. } else {
  43. conn, err = net.Dial("tcp", net.JoinHostPort(ip, "113"))
  44. }
  45. if err != nil {
  46. return
  47. }
  48. defer conn.Close()
  49. // if timeout is 0, `deadline` is the empty time.Time{} which means no deadline:
  50. conn.SetDeadline(deadline)
  51. _, err = conn.Write([]byte(fmt.Sprintf("%d, %d\r\n", portOnClient, portOnServer)))
  52. if err != nil {
  53. return
  54. }
  55. r := bufio.NewReaderSize(conn, 1024)
  56. respBytes, err := r.ReadSlice('\n')
  57. if err != nil {
  58. return
  59. }
  60. resp := string(respBytes)
  61. fields := strings.SplitN(resp, ":", 4)
  62. if len(fields) < 3 {
  63. return response, ProtocolError{resp}
  64. }
  65. for i, field := range fields {
  66. fields[i] = strings.TrimSpace(field)
  67. }
  68. switch fields[1] {
  69. case "USERID":
  70. if len(fields) != 4 {
  71. return response, ProtocolError{resp}
  72. }
  73. var os, charset string
  74. osAndCharset := strings.SplitN(fields[2], ",", 2)
  75. if len(osAndCharset) == 2 {
  76. os = osAndCharset[0]
  77. charset = osAndCharset[1]
  78. } else {
  79. os = osAndCharset[0]
  80. charset = "US-ASCII"
  81. }
  82. return Response{
  83. OS: os,
  84. Charset: charset,
  85. Identifier: fields[3],
  86. }, nil
  87. case "ERROR":
  88. if len(fields) != 3 {
  89. return response, ProtocolError{resp}
  90. }
  91. return response, ResponseError{fields[2]}
  92. default:
  93. err = ProtocolError{resp}
  94. }
  95. return
  96. }