PageRenderTime 51ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/remctl-3.2/portable/getnameinfo.c

#
C | 183 lines | 99 code | 22 blank | 62 comment | 36 complexity | b24a973bb9ddafa2b9201f71b87d819e MD5 | raw file
Possible License(s): ISC
  1. /*
  2. * Replacement for a missing getnameinfo.
  3. *
  4. * This is an implementation of getnameinfo for systems that don't have one so
  5. * that networking code can use a consistant interface without #ifdef. It is
  6. * a fairly minimal implementation, with the following limitations:
  7. *
  8. * - IPv4 support only. IPv6 is not supported.
  9. * - NI_NOFQDN is ignored.
  10. * - Not thread-safe due to gethostbyaddr, getservbyport, and inet_ntoa.
  11. *
  12. * The last two issues could probably be easily remedied, but haven't been
  13. * needed so far. Adding IPv6 support isn't worth it; systems with IPv6
  14. * support should already support getnameinfo natively.
  15. *
  16. * The canonical version of this file is maintained in the rra-c-util package,
  17. * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
  18. *
  19. * Written by Russ Allbery <rra@stanford.edu>
  20. *
  21. * The authors hereby relinquish any claim to any copyright that they may have
  22. * in this work, whether granted under contract or by operation of law or
  23. * international treaty, and hereby commit to the public, at large, that they
  24. * shall not, at any time in the future, seek to enforce any copyright in this
  25. * work against any person or entity, or prevent any person or entity from
  26. * copying, publishing, distributing or creating derivative works of this
  27. * work.
  28. */
  29. #include <config.h>
  30. #include <portable/system.h>
  31. #include <portable/socket.h>
  32. #include <errno.h>
  33. /*
  34. * If we're running the test suite, rename inet_ntoa to avoid conflicts with
  35. * the system version. Note that we don't rename the structures and
  36. * constants, but that should be okay (except possibly for gai_strerror).
  37. */
  38. #if TESTING
  39. # define getnameinfo test_getnameinfo
  40. int test_getnameinfo(const struct sockaddr *, socklen_t, char *, socklen_t,
  41. char *, socklen_t, int);
  42. /* Linux doesn't provide EAI_OVERFLOW, so make up our own for testing. */
  43. # ifndef EAI_OVERFLOW
  44. # define EAI_OVERFLOW 10
  45. # endif
  46. #endif
  47. /* Used for unused parameters to silence gcc warnings. */
  48. #define UNUSED __attribute__((__unused__))
  49. /*
  50. * Check to see if a name is fully qualified by seeing if it contains a
  51. * period. If it does, try to copy it into the provided node buffer and set
  52. * status accordingly, returning true. If not, return false.
  53. */
  54. static int
  55. try_name(const char *name, char *node, socklen_t nodelen, int *status)
  56. {
  57. if (strchr(name, '.') == NULL)
  58. return 0;
  59. if (strlen(name) + 1 > (size_t) nodelen)
  60. *status = EAI_OVERFLOW;
  61. else {
  62. strlcpy(node, name, nodelen);
  63. *status = 0;
  64. }
  65. return 1;
  66. }
  67. /*
  68. * Look up an address (or convert it to ASCII form) and put it in the provided
  69. * buffer, depending on what is requested by flags.
  70. */
  71. static int
  72. lookup_name(const struct in_addr *addr, char *node, socklen_t nodelen,
  73. int flags)
  74. {
  75. struct hostent *host;
  76. char **alias;
  77. int status;
  78. char *name;
  79. /* Do the name lookup first unless told not to. */
  80. if (!(flags & NI_NUMERICHOST)) {
  81. host = gethostbyaddr((const void *) addr, sizeof(struct in_addr),
  82. AF_INET);
  83. if (host == NULL) {
  84. if (flags & NI_NAMEREQD)
  85. return EAI_NONAME;
  86. } else {
  87. if (try_name(host->h_name, node, nodelen, &status))
  88. return status;
  89. for (alias = host->h_aliases; *alias != NULL; alias++)
  90. if (try_name(*alias, node, nodelen, &status))
  91. return status;
  92. }
  93. /*
  94. * We found some results, but none of them were fully-qualified, so
  95. * act as if we found nothing and either fail or fall through.
  96. */
  97. if (flags & NI_NAMEREQD)
  98. return EAI_NONAME;
  99. }
  100. /* Just convert the address to ASCII. */
  101. name = inet_ntoa(*addr);
  102. if (strlen(name) + 1 > (size_t) nodelen)
  103. return EAI_OVERFLOW;
  104. strlcpy(node, name, nodelen);
  105. return 0;
  106. }
  107. /*
  108. * Look up a service (or convert it to ASCII form) and put it in the provided
  109. * buffer, depending on what is requested by flags.
  110. */
  111. static int
  112. lookup_service(unsigned short port, char *service, socklen_t servicelen,
  113. int flags)
  114. {
  115. struct servent *srv;
  116. const char *protocol;
  117. /* Do the name lookup first unless told not to. */
  118. if (!(flags & NI_NUMERICSERV)) {
  119. protocol = (flags & NI_DGRAM) ? "udp" : "tcp";
  120. srv = getservbyport(htons(port), protocol);
  121. if (srv != NULL) {
  122. if (strlen(srv->s_name) + 1 > (size_t) servicelen)
  123. return EAI_OVERFLOW;
  124. strlcpy(service, srv->s_name, servicelen);
  125. return 0;
  126. }
  127. }
  128. /* Just convert the port number to ASCII. */
  129. if ((socklen_t) snprintf(service, servicelen, "%hu", port) > servicelen)
  130. return EAI_OVERFLOW;
  131. return 0;
  132. }
  133. /*
  134. * The getnameinfo implementation.
  135. */
  136. int
  137. getnameinfo(const struct sockaddr *sa, socklen_t salen UNUSED, char *node,
  138. socklen_t nodelen, char *service, socklen_t servicelen, int flags)
  139. {
  140. const struct sockaddr_in *sin;
  141. int status;
  142. unsigned short port;
  143. if ((node == NULL || nodelen <= 0) && (service == NULL || servicelen <= 0))
  144. return EAI_NONAME;
  145. /* We only support AF_INET. */
  146. if (sa->sa_family != AF_INET)
  147. return EAI_FAMILY;
  148. sin = (const struct sockaddr_in *) sa;
  149. /* Name lookup. */
  150. if (node != NULL && nodelen > 0) {
  151. status = lookup_name(&sin->sin_addr, node, nodelen, flags);
  152. if (status != 0)
  153. return status;
  154. }
  155. /* Service lookup. */
  156. if (service != NULL && servicelen > 0) {
  157. port = ntohs(sin->sin_port);
  158. return lookup_service(port, service, servicelen, flags);
  159. } else
  160. return 0;
  161. }