netbios_ns.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. //---------------------------------------------------------------------------
  2. // __________________ _________ _____ _____ .__ ._.
  3. // \______ \______ \ / _____/ / \ / _ \ |__| ____ | |
  4. // | | _/| | \ \_____ \ / \ / \ / /_\ \| _/ __ \ | |
  5. // | | \| ` \/ / Y \ / | | \ ___/ \|
  6. // |______ /_______ /_______ \____|__ / /\ \____|__ |__|\___ | __
  7. // \/ \/ \/ \/ )/ \/ \/ \/
  8. //
  9. // This file is part of libdsm. Copyright © 2014 VideoLabs SAS
  10. //
  11. // Author: Julien 'Lta' BALLET <contact@lta.io>
  12. //
  13. // This program is free software. It comes without any warranty, to the extent
  14. // permitted by applicable law. You can redistribute it and/or modify it under
  15. // the terms of the Do What The Fuck You Want To Public License, Version 2, as
  16. // published by Sam Hocevar. See the COPYING file for more details.
  17. //----------------------------------------------------------------------------
  18. #include <stdlib.h>
  19. #include <stdio.h>
  20. #include <string.h>
  21. #include <unistd.h>
  22. #include <errno.h>
  23. #include <assert.h>
  24. #include <sys/socket.h>
  25. #include <netinet/in.h>
  26. #include <arpa/inet.h>
  27. #include <sys/select.h>
  28. #include "bdsm/debug.h"
  29. #include "bdsm/netbios_ns.h"
  30. #include "bdsm/netbios_query.h"
  31. #include "bdsm/netbios_utils.h"
  32. static int ns_open_socket(netbios_ns_t *ns)
  33. {
  34. int sock_opt;
  35. if ((ns->socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
  36. goto error;
  37. sock_opt = 1;
  38. if (setsockopt(ns->socket, SOL_SOCKET, SO_BROADCAST,
  39. (void *)&sock_opt, sizeof(sock_opt)) < 0)
  40. goto error;
  41. sock_opt = 0;
  42. if (setsockopt(ns->socket, IPPROTO_IP, IP_MULTICAST_LOOP,
  43. (void *)&sock_opt, sizeof(sock_opt)) < 0)
  44. goto error;
  45. ns->addr.sin_family = AF_INET;
  46. ns->addr.sin_port = htons(0);
  47. ns->addr.sin_addr.s_addr = 0;
  48. if (bind(ns->socket, (struct sockaddr *)&ns->addr, sizeof(ns->addr)) < 0)
  49. goto error;
  50. return (1);
  51. error:
  52. perror("netbios_ns_new, open_socket: ");
  53. return (0);
  54. }
  55. netbios_ns_t *netbios_ns_new()
  56. {
  57. netbios_ns_t *ns;
  58. assert(ns = malloc(sizeof(netbios_ns_t)));
  59. memset((void *)ns, 0, sizeof(netbios_ns_t));
  60. if (!ns_open_socket(ns))
  61. {
  62. netbios_ns_destroy(ns);
  63. return (0);
  64. }
  65. ns->entries = NULL;
  66. ns->last_trn_id = rand();
  67. return (ns);
  68. }
  69. void netbios_ns_destroy(netbios_ns_t *ns)
  70. {
  71. netbios_ns_entry_t *next;
  72. if (!ns)
  73. return;
  74. netbios_ns_clear(ns);
  75. close(ns->socket);
  76. free(ns);
  77. }
  78. int netbios_ns_send_query(netbios_ns_t *ns, netbios_query_t *q,
  79. uint32_t ip)
  80. {
  81. struct sockaddr_in addr;
  82. ssize_t sent;
  83. uint16_t trn_id;
  84. assert(ns != NULL && q != NULL);
  85. trn_id = ns->last_trn_id + 1; // Increment transaction ID, not to reuse them
  86. q->packet->trn_id = htons(trn_id);
  87. addr.sin_addr.s_addr = ip;
  88. addr.sin_family = AF_INET;
  89. addr.sin_port = htons(NETBIOS_PORT_NAME);
  90. sent = sendto(ns->socket, (void *)q->packet,
  91. sizeof(netbios_query_packet_t) + q->cursor, 0,
  92. (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
  93. netbios_query_destroy(q);
  94. if (sent < 0){
  95. perror("netbios_ns_send_query: ");
  96. return (0);
  97. }
  98. ns->last_trn_id = trn_id; // Remember the last transaction id.
  99. return (1);
  100. }
  101. ssize_t netbios_ns_recv(int sock, void *buf, size_t buf_size,
  102. struct timeval *timeout, struct sockaddr *addr,
  103. socklen_t *addr_len)
  104. {
  105. fd_set read_fds, error_fds;
  106. int res;
  107. assert(sock && buf != NULL && buf_size > 0);
  108. FD_ZERO(&read_fds);
  109. FD_ZERO(&error_fds);
  110. FD_SET(sock, &read_fds);
  111. FD_SET(sock, &error_fds);
  112. res = select(sock + 1, &read_fds, 0, &error_fds, timeout);
  113. if (res < 0)
  114. goto error;
  115. if (FD_ISSET(sock, &error_fds))
  116. goto error;
  117. if (FD_ISSET(sock, &read_fds))
  118. {
  119. return (recvfrom(sock, buf, buf_size, 0, addr, addr_len));
  120. }
  121. else
  122. return (0);
  123. error:
  124. perror("netbios_ns_recv: ");
  125. return (-1);
  126. }
  127. int netbios_ns_resolve(netbios_ns_t *ns, const char *name, char type, uint32_t * addr)
  128. {
  129. netbios_ns_entry_t *cached;
  130. struct timeval timeout;
  131. netbios_query_t *q;
  132. char *encoded_name;
  133. char footer[4] = { 0x00, 0x20, 0x00, 0x01 };
  134. char recv_buffer[512]; // Hu ?
  135. ssize_t recv;
  136. uint16_t trn_id;
  137. uint32_t ip;
  138. assert(ns != NULL);
  139. if ((cached = netbios_ns_entry_find(ns, name, 0)) != NULL)
  140. return (cached->address.s_addr);
  141. if ((encoded_name = netbios_name_encode(name, 0, type)) == NULL)
  142. return (-1);
  143. // Prepare packet
  144. q = netbios_query_new(34 + 4, 1, NETBIOS_OP_NAME_QUERY);
  145. netbios_query_set_flag(q, NETBIOS_FLAG_RECURSIVE, 1);
  146. netbios_query_set_flag(q, NETBIOS_FLAG_BROADCAST, 1);
  147. // Append the queried name to the packet
  148. netbios_query_append(q, encoded_name, strlen(encoded_name) + 1);
  149. // Magic footer (i.e. Question type (Netbios) / class (IP)
  150. netbios_query_append(q, footer, 4);
  151. q->packet->queries = htons(1);
  152. // We broadcast this query
  153. inet_aton("255.255.255.255", (struct in_addr *)&ip);
  154. // Let's send it
  155. if (!netbios_ns_send_query(ns, q, ip))
  156. return (0);
  157. else
  158. BDSM_dbg("netbios_ns_resolve, name query sent for '%s' !\n", name);
  159. free(encoded_name);
  160. // Now wait for a reply and pray
  161. timeout.tv_sec = 2;
  162. timeout.tv_usec = 420;
  163. recv = netbios_ns_recv(ns->socket, (void *)recv_buffer, 512, &timeout, 0, 0);
  164. if (recv <= 0)
  165. perror("netbios_ns_resolve:");
  166. else if (recv == 0)
  167. BDSM_dbg("netbios_ns_resolve, received NO reply for '%s' !\n", name);
  168. else
  169. {
  170. BDSM_dbg("netbios_ns_resolve, received a reply for '%s' !\n", name);
  171. *addr = (*(uint32_t *)(recv_buffer + recv - 4));
  172. return (1);
  173. }
  174. return (0);
  175. }
  176. // We have a small recursive function for discovery, to stack received reply
  177. // when descending, and performing reverse lookup when ascending
  178. static void netbios_ns_discover_rec(netbios_ns_t *ns, struct timeval *timeout,
  179. void *recv_buffer)
  180. {
  181. struct sockaddr_in recv_addr;
  182. socklen_t recv_addr_len;
  183. int res;
  184. recv_addr_len = sizeof(recv_addr);
  185. res = netbios_ns_recv(ns->socket, recv_buffer, 256, timeout,
  186. (struct sockaddr *)&recv_addr, &recv_addr_len);
  187. if (res > 0 && timeout->tv_sec && timeout->tv_usec)
  188. {
  189. netbios_ns_discover_rec(ns, timeout, recv_buffer);
  190. BDSM_dbg("Discover: received a reply from %s\n",
  191. inet_ntoa(recv_addr.sin_addr));
  192. netbios_ns_inverse(ns, recv_addr.sin_addr.s_addr);
  193. }
  194. }
  195. int netbios_ns_discover(netbios_ns_t *ns)
  196. {
  197. const char broadcast_name[] = NETBIOS_WILDCARD;
  198. char footer[4] = { 0x00, 0x20, 0x00, 0x01 };
  199. struct timeval timeout;
  200. netbios_query_t *q;
  201. char recv_buffer[256]; // Hu ?
  202. ssize_t recv;
  203. uint32_t ip;
  204. assert (ns != NULL);
  205. //
  206. // First step, we broadcast a packet to receive a message from every
  207. // NETBIOS nodes on the local network
  208. //
  209. q = netbios_query_new(34 + 4, 1, NETBIOS_OP_NAME_QUERY);
  210. // Append the queried name to the packet
  211. netbios_query_append(q, broadcast_name, strlen(broadcast_name) + 1);
  212. // Magic footer (i.e. Question type (Netbios) / class (IP)
  213. netbios_query_append(q, footer, 4);
  214. q->packet->queries = htons(1);
  215. // We broadcast this query
  216. inet_aton("255.255.255.255", (struct in_addr *)&ip);
  217. // Let's send it
  218. if (!netbios_ns_send_query(ns, q, ip))
  219. {
  220. BDSM_dbg("Unable to send netbios 'discovery query'.\n");
  221. return (0);
  222. }
  223. else
  224. BDSM_dbg("netbios_ns_discover, name query sent for '*'.\n");
  225. //
  226. // Second step, we list every IP that answered to our broadcast.
  227. //
  228. timeout.tv_sec = 2;
  229. timeout.tv_usec = 420;
  230. netbios_ns_discover_rec(ns, &timeout, (void *)recv_buffer);
  231. return (1);
  232. }
  233. // Perform inverse name resolution. Grap an IP and return the first <20> field
  234. // returned by the host
  235. const char *netbios_ns_inverse(netbios_ns_t *ns, uint32_t ip)
  236. {
  237. const char broadcast_name[] = NETBIOS_WILDCARD;
  238. char footer[4] = { 0x00, 0x21, 0x00, 0x01 }; // NBSTAT/IP
  239. netbios_ns_entry_t *cached;
  240. struct timeval timeout;
  241. netbios_query_t *q;
  242. char recv_buffer[512]; // Hu ?
  243. ssize_t recv;
  244. assert(ns != NULL && ip != 0);
  245. if ((cached = netbios_ns_entry_find(ns, NULL, ip)) != NULL)
  246. return (cached->name);
  247. // Prepare NBSTAT query packet
  248. q = netbios_query_new(34 + 4, 1, NETBIOS_OP_NAME_QUERY);
  249. netbios_query_append(q, broadcast_name, strlen(broadcast_name) + 1);
  250. netbios_query_append(q, footer, 4);
  251. q->packet->queries = htons(1);
  252. // Let's send it
  253. if (!netbios_ns_send_query(ns, q, ip))
  254. return (NULL);
  255. else
  256. BDSM_dbg("netbios_ns_inverse, reverse name query sent for '%s' !\n",
  257. inet_ntoa(*(struct in_addr *)&ip));
  258. // Now wait for a reply and pray
  259. timeout.tv_sec = 1;
  260. timeout.tv_usec = 500;
  261. recv = netbios_ns_recv(ns->socket, (void *)recv_buffer, 512, &timeout, 0, 0);
  262. if (recv <= 0)
  263. goto error;
  264. else
  265. BDSM_dbg("netbios_ns_inverse, received a reply for '%s' !\n",
  266. inet_ntoa(*(struct in_addr*)&ip));
  267. // Now we've got something, let's find the <20>/<0> name
  268. netbios_query_packet_t *p = (netbios_query_packet_t *)recv_buffer;
  269. uint8_t name_count;
  270. uint8_t name_idx;
  271. char *names;
  272. char *current_name;
  273. char current_type;
  274. netbios_ns_entry_t *entry = NULL, *res = NULL;
  275. BDSM_dbg("Queried name length: %u\n", p->payload[0]);
  276. name_count = p->payload[p->payload[0] + 12];
  277. BDSM_dbg("Number of names: %hhu\n", name_count);
  278. names = p->payload + p->payload[0] + 13;
  279. for(name_idx = 0; name_idx < name_count; name_idx++)
  280. {
  281. current_name = names + name_idx * 18;
  282. current_type = current_name[15];
  283. BDSM_dbg("Found name : %s (type == 0x%x)\n", current_name, current_type);
  284. if (current_type == 0x20 || current_type == 0)
  285. entry = netbios_ns_entry_add(ns, current_name, current_type, ip);
  286. if (current_type == 0x20)
  287. res = entry;
  288. }
  289. if (res) // We prefer a <20> name.
  290. return(res->name);
  291. else if (entry) // Did we found a <0> or <20> name ?
  292. return(entry->name);
  293. else
  294. return (NULL);
  295. error:
  296. perror("netbios_ns_inverse: ");
  297. return (NULL);
  298. }