netbios_ns.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903
  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 <stdbool.h>
  22. #include <pthread.h>
  23. #include <sys/time.h>
  24. #include <sys/queue.h>
  25. #include <unistd.h>
  26. #include <fcntl.h>
  27. #include <errno.h>
  28. #include <assert.h>
  29. #include <sys/socket.h>
  30. #include <netinet/in.h>
  31. #include <arpa/inet.h>
  32. #include <sys/select.h>
  33. #include <sys/queue.h>
  34. #include <bdsm/netbios_ns.h>
  35. #include "bdsm_debug.h"
  36. #include "netbios_query.h"
  37. #include "netbios_utils.h"
  38. #define DISCOVER_REMOVE_TIMEOUT 60 // in sec
  39. enum name_query_type {
  40. NAME_QUERY_TYPE_INVALID,
  41. NAME_QUERY_TYPE_NB,
  42. NAME_QUERY_TYPE_NBSTAT
  43. };
  44. static char name_query_broadcast[] = NETBIOS_WILDCARD;
  45. enum ns_entry_flag {
  46. NS_ENTRY_FLAG_INVALID = 0x00,
  47. NS_ENTRY_FLAG_VALID_IP = 0x01,
  48. NS_ENTRY_FLAG_VALID_NAME = 0x02,
  49. };
  50. struct netbios_ns_entry
  51. {
  52. TAILQ_ENTRY(netbios_ns_entry) next;
  53. struct in_addr address;
  54. char name[NETBIOS_NAME_LENGTH + 1];
  55. char group[NETBIOS_NAME_LENGTH + 1];
  56. char type;
  57. int flag;
  58. time_t last_time_seen;
  59. };
  60. typedef TAILQ_HEAD(, netbios_ns_entry) NS_ENTRY_QUEUE;
  61. #define RECV_BUFFER_SIZE 1500 // Max MTU frame size for ethernet
  62. struct netbios_ns
  63. {
  64. int socket;
  65. struct sockaddr_in addr;
  66. uint16_t last_trn_id; // Last transaction id used;
  67. NS_ENTRY_QUEUE entry_queue;
  68. uint8_t buffer[RECV_BUFFER_SIZE];
  69. int abort_pipe[2];
  70. unsigned int discover_broadcast_timeout;
  71. pthread_t discover_thread;
  72. bool discover_started;
  73. netbios_ns_discover_callbacks discover_callbacks;
  74. };
  75. typedef struct netbios_ns_name_query netbios_ns_name_query;
  76. struct netbios_ns_name_query
  77. {
  78. enum name_query_type type;
  79. union {
  80. struct {
  81. uint32_t ip;
  82. } nb;
  83. struct {
  84. const char *name;
  85. const char *group;
  86. char type;
  87. } nbstat;
  88. }u;
  89. };
  90. static netbios_ns_entry *netbios_ns_inverse_internal(netbios_ns *ns,
  91. uint32_t ip);
  92. static int ns_open_socket(netbios_ns *ns)
  93. {
  94. int sock_opt;
  95. if ((ns->socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
  96. goto error;
  97. sock_opt = 1;
  98. if (setsockopt(ns->socket, SOL_SOCKET, SO_BROADCAST,
  99. (void *)&sock_opt, sizeof(sock_opt)) < 0)
  100. goto error;
  101. sock_opt = 0;
  102. if (setsockopt(ns->socket, IPPROTO_IP, IP_MULTICAST_LOOP,
  103. (void *)&sock_opt, sizeof(sock_opt)) < 0)
  104. goto error;
  105. ns->addr.sin_family = AF_INET;
  106. ns->addr.sin_port = htons(0);
  107. ns->addr.sin_addr.s_addr = 0;
  108. if (bind(ns->socket, (struct sockaddr *)&ns->addr, sizeof(ns->addr)) < 0)
  109. goto error;
  110. return (1);
  111. error:
  112. BDSM_perror("netbios_ns_new, open_socket: ");
  113. return (0);
  114. }
  115. static int ns_open_abort_pipe(netbios_ns *ns)
  116. {
  117. int flags;
  118. if (pipe(ns->abort_pipe) == -1)
  119. return -1;
  120. if ((flags = fcntl(ns->abort_pipe[0], F_GETFL, 0)) == -1)
  121. return -1;
  122. if (fcntl(ns->abort_pipe[0], F_SETFL, flags | O_NONBLOCK) == -1)
  123. return -1;
  124. return 0;
  125. }
  126. static void ns_close_abort_pipe(netbios_ns *ns)
  127. {
  128. if (ns->abort_pipe[0] != -1 && ns->abort_pipe[1] != -1)
  129. {
  130. close(ns->abort_pipe[0]);
  131. close(ns->abort_pipe[1]);
  132. ns->abort_pipe[0] = ns->abort_pipe[1] -1;
  133. }
  134. }
  135. static uint16_t query_type_nb = 0x2000;
  136. static uint16_t query_type_nbstat = 0x2100;
  137. static uint16_t query_class_in = 0x0100;
  138. static int netbios_ns_send_name_query(netbios_ns *ns,
  139. uint32_t ip,
  140. enum name_query_type type,
  141. const char *name,
  142. uint16_t query_flag)
  143. {
  144. struct sockaddr_in addr;
  145. ssize_t sent;
  146. uint16_t trn_id;
  147. uint16_t query_type;
  148. netbios_query *q;
  149. assert(ns != NULL);
  150. switch (type)
  151. {
  152. case NAME_QUERY_TYPE_NB:
  153. query_type = query_type_nb;
  154. break;
  155. case NAME_QUERY_TYPE_NBSTAT:
  156. query_type = query_type_nbstat; // NBSTAT/IP;
  157. break;
  158. default:
  159. BDSM_dbg("netbios_ns_send_name_query: unknow name_query_type");
  160. return -1;
  161. }
  162. // Prepare packet
  163. q = netbios_query_new(34 + 4, 1, NETBIOS_OP_NAME_QUERY);
  164. if (query_flag)
  165. netbios_query_set_flag(q, query_flag, 1);
  166. // Append the queried name to the packet
  167. netbios_query_append(q, name, strlen(name) + 1);
  168. // Magic footer (i.e. Question type (Netbios) / class (IP)
  169. netbios_query_append(q, (const char *)&query_type, 2);
  170. netbios_query_append(q, (const char *)&query_class_in, 2);
  171. q->packet->queries = htons(1);
  172. if (ip == 0)
  173. inet_aton("255.255.255.255", (struct in_addr *)&ip);
  174. trn_id = ns->last_trn_id + 1; // Increment transaction ID, not to reuse them
  175. q->packet->trn_id = htons(trn_id);
  176. addr.sin_addr.s_addr = ip;
  177. addr.sin_family = AF_INET;
  178. addr.sin_port = htons(NETBIOS_PORT_NAME);
  179. sent = sendto(ns->socket, (void *)q->packet,
  180. sizeof(netbios_query_packet) + q->cursor, 0,
  181. (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
  182. netbios_query_destroy(q);
  183. if (sent < 0)
  184. {
  185. BDSM_perror("netbios_ns_send_name_query: ");
  186. return -1;
  187. }
  188. else
  189. BDSM_dbg("netbios_ns_send_name_query, name query sent for '*'.\n");
  190. ns->last_trn_id = trn_id; // Remember the last transaction id.
  191. return 0;
  192. }
  193. static int netbios_ns_handle_query(netbios_ns *ns, size_t size,
  194. bool check_trn_id, uint32_t recv_ip,
  195. netbios_ns_name_query *out_name_query)
  196. {
  197. netbios_query_packet *q;
  198. uint8_t name_size;
  199. uint16_t *p_type, type;
  200. uint16_t *p_data_length, data_length;
  201. char *p_data;
  202. // check for packet size
  203. if (size < sizeof(netbios_query_packet))
  204. {
  205. BDSM_dbg("netbios_ns_handle_query, invalid size !\n");
  206. return -1;
  207. }
  208. q = (netbios_query_packet *)ns->buffer;
  209. if (check_trn_id)
  210. {
  211. // check if trn_id corresponds
  212. if (ntohs(q->trn_id) != ns->last_trn_id) {
  213. BDSM_dbg("netbios_ns_handle_query, invalid trn_id: %d vs %d\n",
  214. ntohs(q->trn_id), ns->last_trn_id);
  215. return -1;
  216. }
  217. }
  218. if (!out_name_query)
  219. return 0;
  220. // get Name size, should be 0x20
  221. if (size < sizeof(netbios_query_packet) + 1)
  222. return -1;
  223. name_size = q->payload[0];
  224. if (name_size != 0x20)
  225. return -1;
  226. // get type and data_length
  227. if (size < sizeof(netbios_query_packet) + name_size + 11)
  228. return -1;
  229. p_type = (uint16_t *) (q->payload + name_size + 2);
  230. type = *p_type;
  231. p_data_length = (uint16_t *) (q->payload + name_size + 10);
  232. data_length = ntohs(*p_data_length);
  233. if (size < sizeof(netbios_query_packet) + name_size + 12 + data_length)
  234. return -1;
  235. p_data = q->payload + name_size + 12;
  236. if (type == query_type_nb) {
  237. uint32_t ip;
  238. // get ip addr
  239. if (data_length < 6)
  240. ip = recv_ip;
  241. else
  242. {
  243. uint32_t *p_ip = (uint32_t *) (p_data + 2);
  244. ip = *p_ip;
  245. }
  246. out_name_query->type = NAME_QUERY_TYPE_NB;
  247. out_name_query->u.nb.ip = ip;
  248. } else if (type == query_type_nbstat) {
  249. uint8_t name_count;
  250. const char *names = p_data + 1;
  251. const char *group = NULL, *name = NULL;;
  252. // get the number of names
  253. if (data_length < 1)
  254. return -1;
  255. name_count = *(p_data);
  256. names = p_data + 1;
  257. if (data_length < name_count * 18)
  258. return -1;
  259. // first search for a group in the name list
  260. for (uint8_t name_idx = 0; name_idx < name_count; name_idx++)
  261. {
  262. const char *current_name = names + name_idx * 18;
  263. uint16_t current_flags = (current_name[16] << 8) | current_name[17];
  264. if (current_flags & NETBIOS_NAME_FLAG_GROUP) {
  265. group = current_name;
  266. break;
  267. }
  268. }
  269. // then search for file servers
  270. for (uint8_t name_idx = 0; name_idx < name_count; name_idx++)
  271. {
  272. const char *current_name = names + name_idx * 18;
  273. char current_type = current_name[15];
  274. uint16_t current_flags = (current_name[16] << 8) | current_name[17];
  275. if (current_flags & NETBIOS_NAME_FLAG_GROUP)
  276. continue;
  277. if (current_type == NETBIOS_FILESERVER)
  278. {
  279. name = current_name;
  280. BDSM_dbg("netbios_ns_handle_query, Found name: '%.*s' in group: '%.*s'\n",
  281. NETBIOS_NAME_LENGTH, name, NETBIOS_NAME_LENGTH, group);
  282. break;
  283. }
  284. }
  285. if (name)
  286. {
  287. out_name_query->type = NAME_QUERY_TYPE_NBSTAT;
  288. out_name_query->u.nbstat.name = name;
  289. out_name_query->u.nbstat.group = group;
  290. out_name_query->u.nbstat.type = NETBIOS_FILESERVER;
  291. }
  292. }
  293. return 0;
  294. }
  295. static ssize_t netbios_ns_recv(netbios_ns *ns,
  296. struct timeval *timeout,
  297. struct sockaddr_in *out_addr,
  298. bool check_trn_id,
  299. uint32_t wait_ip,
  300. netbios_ns_name_query *out_name_query)
  301. {
  302. int sock, abort_fd;
  303. assert(ns != NULL);
  304. sock = ns->socket;
  305. abort_fd = ns->abort_pipe[0];
  306. if (out_name_query)
  307. out_name_query->type = NAME_QUERY_TYPE_INVALID;
  308. while (true)
  309. {
  310. fd_set read_fds, error_fds;
  311. int res, nfds;
  312. FD_ZERO(&read_fds);
  313. FD_ZERO(&error_fds);
  314. FD_SET(sock, &read_fds);
  315. FD_SET(abort_fd, &read_fds);
  316. FD_SET(sock, &error_fds);
  317. nfds = (sock > abort_fd ? sock : abort_fd) + 1;
  318. res = select(nfds, &read_fds, 0, &error_fds, timeout);
  319. if (res < 0)
  320. goto error;
  321. if (FD_ISSET(sock, &error_fds))
  322. goto error;
  323. if (FD_ISSET(abort_fd, &read_fds))
  324. return -1;
  325. else if (FD_ISSET(sock, &read_fds))
  326. {
  327. struct sockaddr_in addr;
  328. socklen_t addr_len = sizeof(struct sockaddr_in);
  329. ssize_t size;
  330. size = recvfrom(sock, ns->buffer, RECV_BUFFER_SIZE, 0,
  331. (struct sockaddr *)&addr, &addr_len);
  332. if (size < 0)
  333. return -1;
  334. if (wait_ip != 0 && addr_len >= sizeof(struct sockaddr_in))
  335. {
  336. // wait for a reply from a specific ip
  337. if (wait_ip != addr.sin_addr.s_addr)
  338. {
  339. BDSM_dbg("netbios_ns_recv, invalid ip");
  340. continue;
  341. }
  342. }
  343. if (netbios_ns_handle_query(ns, (size_t)size, check_trn_id,
  344. addr.sin_addr.s_addr,
  345. out_name_query) == -1)
  346. {
  347. BDSM_dbg("netbios_ns_recv, invalid query\n");
  348. continue;
  349. }
  350. if (out_addr)
  351. *out_addr = addr;
  352. return size;
  353. }
  354. else
  355. return (0);
  356. }
  357. error:
  358. BDSM_perror("netbios_ns_recv: ");
  359. return (-1);
  360. }
  361. static void netbios_ns_copy_name(char *dest, const char *src)
  362. {
  363. memcpy(dest, src, NETBIOS_NAME_LENGTH);
  364. dest[NETBIOS_NAME_LENGTH] = 0;
  365. for (int i = 1; i < NETBIOS_NAME_LENGTH; i++ )
  366. if (dest[NETBIOS_NAME_LENGTH - i] == ' ')
  367. dest[NETBIOS_NAME_LENGTH - i] = 0;
  368. else
  369. break;
  370. }
  371. static void netbios_ns_entry_set_name(netbios_ns_entry *entry,
  372. const char *name, const char *group,
  373. char type)
  374. {
  375. if (name != NULL)
  376. netbios_ns_copy_name(entry->name, name);
  377. if (group != NULL)
  378. netbios_ns_copy_name(entry->group, group);
  379. entry->type = type;
  380. entry->flag |= NS_ENTRY_FLAG_VALID_NAME;
  381. }
  382. static netbios_ns_entry *netbios_ns_entry_add(netbios_ns *ns, uint32_t ip)
  383. {
  384. netbios_ns_entry *entry;
  385. entry = calloc(1, sizeof(netbios_ns_entry));
  386. if (!entry)
  387. return NULL;
  388. entry->address.s_addr = ip;
  389. entry->flag |= NS_ENTRY_FLAG_VALID_IP;
  390. TAILQ_INSERT_HEAD(&ns->entry_queue, entry, next);
  391. return entry;
  392. }
  393. // Find an entry in the list. Search by name if name is not NULL,
  394. // or by ip otherwise
  395. static netbios_ns_entry *netbios_ns_entry_find(netbios_ns *ns, const char *by_name,
  396. uint32_t ip)
  397. {
  398. netbios_ns_entry *iter;
  399. assert(ns != NULL);
  400. TAILQ_FOREACH(iter, &ns->entry_queue, next)
  401. {
  402. if (by_name != NULL)
  403. {
  404. if (iter->flag & NS_ENTRY_FLAG_VALID_NAME
  405. && !strncmp(by_name, iter->name, NETBIOS_NAME_LENGTH))
  406. return iter;
  407. }
  408. else if (iter->flag & NS_ENTRY_FLAG_VALID_IP
  409. && iter->address.s_addr == ip)
  410. return iter;
  411. }
  412. return NULL;
  413. }
  414. netbios_ns *netbios_ns_new()
  415. {
  416. netbios_ns *ns;
  417. ns = calloc(1, sizeof(netbios_ns));
  418. if (!ns)
  419. return NULL;
  420. ns->abort_pipe[0] = ns->abort_pipe[1] -1;
  421. if (!ns_open_socket(ns) || ns_open_abort_pipe(ns) == -1)
  422. {
  423. netbios_ns_destroy(ns);
  424. return (0);
  425. }
  426. TAILQ_INIT(&ns->entry_queue);
  427. ns->last_trn_id = rand();
  428. return (ns);
  429. }
  430. void netbios_ns_destroy(netbios_ns *ns)
  431. {
  432. if (!ns)
  433. return;
  434. netbios_ns_clear(ns);
  435. close(ns->socket);
  436. ns_close_abort_pipe(ns);
  437. free(ns);
  438. }
  439. int netbios_ns_resolve(netbios_ns *ns, const char *name, char type, uint32_t *addr)
  440. {
  441. netbios_ns_entry *cached;
  442. struct timeval timeout;
  443. char *encoded_name;
  444. ssize_t recv;
  445. netbios_ns_name_query name_query;
  446. assert(ns != NULL && !ns->discover_started);
  447. if ((cached = netbios_ns_entry_find(ns, name, 0)) != NULL)
  448. {
  449. *addr = cached->address.s_addr;
  450. return (1);
  451. }
  452. if ((encoded_name = netbios_name_encode(name, 0, type)) == NULL)
  453. return (0);
  454. if (netbios_ns_send_name_query(ns, 0, NAME_QUERY_TYPE_NB, encoded_name,
  455. NETBIOS_FLAG_RECURSIVE |
  456. NETBIOS_FLAG_BROADCAST) == -1)
  457. {
  458. free(encoded_name);
  459. return (0);
  460. }
  461. free(encoded_name);
  462. // Now wait for a reply and pray
  463. timeout.tv_sec = 2;
  464. timeout.tv_usec = 420;
  465. recv = netbios_ns_recv(ns, &timeout, NULL, true, 0, &name_query);
  466. if (recv < 0)
  467. BDSM_perror("netbios_ns_resolve:");
  468. else
  469. {
  470. if (name_query.type == NAME_QUERY_TYPE_NB)
  471. {
  472. *addr = name_query.u.nb.ip;
  473. BDSM_dbg("netbios_ns_resolve, received a reply for '%s', ip: 0x%X!\n", name, *addr);
  474. return (1);
  475. } else
  476. BDSM_dbg("netbios_ns_resolve, wrong query type received\n");
  477. }
  478. return (0);
  479. }
  480. // We have a small recursive function for discovery, to stack received reply
  481. // when descending, and performing reverse lookup when ascending
  482. static void netbios_ns_discover_rec(netbios_ns *ns, struct timeval *timeout )
  483. {
  484. struct sockaddr_in recv_addr;
  485. int res;
  486. res = netbios_ns_recv(ns, timeout, &recv_addr, true, 0, NULL);
  487. if (res > 0 && timeout->tv_sec && timeout->tv_usec)
  488. {
  489. netbios_ns_discover_rec(ns, timeout);
  490. BDSM_dbg("Discover: received a reply from %s\n",
  491. inet_ntoa(recv_addr.sin_addr));
  492. netbios_ns_inverse_internal(ns, recv_addr.sin_addr.s_addr);
  493. }
  494. }
  495. int netbios_ns_discover(netbios_ns *ns)
  496. {
  497. struct timeval timeout;
  498. assert(ns != NULL);
  499. //
  500. // First step, we broadcast a packet to receive a message from every
  501. // NETBIOS nodes on the local network
  502. //
  503. if (netbios_ns_send_name_query(ns, 0, NAME_QUERY_TYPE_NB,
  504. name_query_broadcast, 0) == -1)
  505. return (0);
  506. //
  507. // Second step, we list every IP that answered to our broadcast.
  508. //
  509. timeout.tv_sec = 2;
  510. timeout.tv_usec = 420;
  511. netbios_ns_discover_rec(ns, &timeout);
  512. return (1);
  513. }
  514. static int netbios_ns_is_aborted(netbios_ns *ns)
  515. {
  516. fd_set read_fds;
  517. int res;
  518. struct timeval timeout = {0, 0};
  519. FD_ZERO(&read_fds);
  520. FD_SET(ns->abort_pipe[0], &read_fds);
  521. res = select(ns->abort_pipe[0] + 1, &read_fds, NULL, NULL, &timeout);
  522. return (res < 0 || FD_ISSET(ns->abort_pipe[0], &read_fds)) ? 1 : 0;
  523. }
  524. void netbios_ns_abort(netbios_ns *ns)
  525. {
  526. uint8_t buf = '\0';
  527. write(ns->abort_pipe[1], &buf, sizeof(uint8_t));
  528. }
  529. // Perform inverse name resolution. Grap an IP and return the first <20> field
  530. // returned by the host
  531. static netbios_ns_entry *netbios_ns_inverse_internal(netbios_ns *ns, uint32_t ip)
  532. {
  533. netbios_ns_entry *cached;
  534. struct timeval timeout;
  535. ssize_t recv;
  536. netbios_ns_name_query name_query;
  537. netbios_ns_entry *entry;
  538. if ((cached = netbios_ns_entry_find(ns, NULL, ip)) != NULL)
  539. return (cached);
  540. if (netbios_ns_send_name_query(ns, ip, NAME_QUERY_TYPE_NBSTAT,
  541. name_query_broadcast, 0) == -1)
  542. goto error;
  543. // Now wait for a reply and pray
  544. timeout.tv_sec = 1;
  545. timeout.tv_usec = 500;
  546. recv = netbios_ns_recv(ns, &timeout, NULL, true, ip, &name_query);
  547. if (recv <= 0)
  548. goto error;
  549. if (name_query.type != NAME_QUERY_TYPE_NBSTAT)
  550. {
  551. BDSM_dbg("netbios_ns_inverse, wrong query type received\n");
  552. goto error;
  553. }
  554. else
  555. BDSM_dbg("netbios_ns_inverse, received a reply for '%s' !\n",
  556. inet_ntoa(*(struct in_addr *)&ip));
  557. entry = netbios_ns_entry_add(ns, ip);
  558. if (entry)
  559. netbios_ns_entry_set_name(entry, name_query.u.nbstat.name,
  560. name_query.u.nbstat.group,
  561. name_query.u.nbstat.type);
  562. return entry;
  563. error:
  564. BDSM_perror("netbios_ns_inverse: ");
  565. return (NULL);
  566. }
  567. const char *netbios_ns_inverse(netbios_ns *ns, uint32_t ip)
  568. {
  569. assert(ns != NULL && ip != 0 && !ns->discover_started);
  570. netbios_ns_entry *entry = netbios_ns_inverse_internal(ns, ip);
  571. return entry ? entry->name : NULL;
  572. }
  573. const char *netbios_ns_entry_name(netbios_ns_entry *entry)
  574. {
  575. return entry ? entry->name : NULL;
  576. }
  577. const char *netbios_ns_entry_group(netbios_ns_entry *entry)
  578. {
  579. return entry ? entry->group : NULL;
  580. }
  581. uint32_t netbios_ns_entry_ip(netbios_ns_entry *entry)
  582. {
  583. return entry ? entry->address.s_addr : 0;
  584. }
  585. char netbios_ns_entry_type(netbios_ns_entry *entry)
  586. {
  587. return entry ? entry->type : -1;
  588. }
  589. void netbios_ns_clear(netbios_ns *ns)
  590. {
  591. netbios_ns_entry *entry, *entry_next;
  592. assert(ns != NULL);
  593. for (entry = TAILQ_FIRST(&ns->entry_queue);
  594. entry != NULL; entry = entry_next)
  595. {
  596. entry_next = TAILQ_NEXT(entry, next);
  597. TAILQ_REMOVE(&ns->entry_queue, entry, next);
  598. free(entry);
  599. }
  600. }
  601. int netbios_ns_entry_count(netbios_ns *ns)
  602. {
  603. netbios_ns_entry *iter;
  604. int res;
  605. assert(ns != NULL);
  606. res = 0;
  607. TAILQ_FOREACH(iter, &ns->entry_queue, next)
  608. {
  609. res++;
  610. }
  611. return (res);
  612. }
  613. netbios_ns_entry *netbios_ns_entry_at(netbios_ns *ns, int pos)
  614. {
  615. netbios_ns_entry *iter = NULL;
  616. int i = 0;
  617. assert(ns != NULL);
  618. iter = TAILQ_FIRST(&ns->entry_queue);
  619. while (i < pos && iter != NULL)
  620. {
  621. i++;
  622. iter = TAILQ_NEXT(iter, next);
  623. }
  624. return (iter);
  625. }
  626. static void *netbios_ns_discover_thread(void *opaque)
  627. {
  628. netbios_ns *ns = (netbios_ns *) opaque;
  629. while (true)
  630. {
  631. struct timespec tp;
  632. netbios_ns_entry *entry, *entry_next;
  633. if (netbios_ns_is_aborted(ns))
  634. return NULL;
  635. // check if cached entries timeout
  636. clock_gettime(CLOCK_REALTIME, &tp);
  637. for (entry = TAILQ_FIRST(&ns->entry_queue);
  638. entry != NULL; entry = entry_next)
  639. {
  640. entry_next = TAILQ_NEXT(entry, next);
  641. if (tp.tv_sec - entry->last_time_seen > DISCOVER_REMOVE_TIMEOUT)
  642. {
  643. BDSM_dbg("Discover: on_entry_removed: %s\n", entry->name);
  644. ns->discover_callbacks.pf_on_entry_removed(
  645. ns->discover_callbacks.p_opaque, entry);
  646. TAILQ_REMOVE(&ns->entry_queue, entry, next);
  647. free(entry);
  648. }
  649. }
  650. // send broadbast
  651. if (netbios_ns_send_name_query(ns, 0, NAME_QUERY_TYPE_NB,
  652. name_query_broadcast, 0) == -1)
  653. return NULL;
  654. while (true)
  655. {
  656. struct timeval timeout;
  657. struct sockaddr_in recv_addr;
  658. int res;
  659. netbios_ns_name_query name_query;
  660. timeout.tv_sec = ns->discover_broadcast_timeout;
  661. timeout.tv_usec = 0;
  662. // receive NB or NBSTAT answers
  663. res = netbios_ns_recv(ns, ns->discover_broadcast_timeout == 0 ?
  664. NULL : &timeout,
  665. &recv_addr,
  666. false, 0, &name_query);
  667. // error or abort
  668. if (res == -1)
  669. return NULL;
  670. // timeout reached, broadcast again
  671. if (res == 0)
  672. break;
  673. clock_gettime(CLOCK_REALTIME, &tp);
  674. if (name_query.type == NAME_QUERY_TYPE_NB)
  675. {
  676. uint32_t ip = name_query.u.nb.ip;
  677. entry = netbios_ns_entry_find(ns, NULL, ip);
  678. if (!entry)
  679. {
  680. entry = netbios_ns_entry_add(ns, ip);
  681. if (!entry)
  682. return NULL;
  683. }
  684. entry->last_time_seen = tp.tv_sec;
  685. // if entry is already valid, don't send NBSTAT query
  686. if (entry->flag & NS_ENTRY_FLAG_VALID_NAME)
  687. continue;
  688. // send NBSTAT query
  689. if (netbios_ns_send_name_query(ns, ip, NAME_QUERY_TYPE_NBSTAT,
  690. name_query_broadcast, 0) == -1)
  691. return NULL;
  692. }
  693. else if (name_query.type == NAME_QUERY_TYPE_NBSTAT)
  694. {
  695. bool send_callback;
  696. entry = netbios_ns_entry_find(ns, NULL,
  697. recv_addr.sin_addr.s_addr);
  698. // ignore NBSTAT answers that didn't answered to NB query first.
  699. if (!entry)
  700. continue;
  701. entry->last_time_seen = tp.tv_sec;
  702. send_callback = !(entry->flag & NS_ENTRY_FLAG_VALID_NAME);
  703. netbios_ns_entry_set_name(entry, name_query.u.nbstat.name,
  704. name_query.u.nbstat.group,
  705. name_query.u.nbstat.type);
  706. if (send_callback)
  707. ns->discover_callbacks.pf_on_entry_added(
  708. ns->discover_callbacks.p_opaque, entry);
  709. }
  710. }
  711. if (ns->discover_broadcast_timeout == 0)
  712. break;
  713. }
  714. return NULL;
  715. }
  716. int netbios_ns_discover_start(netbios_ns *ns,
  717. unsigned int broadcast_timeout,
  718. netbios_ns_discover_callbacks *callbacks)
  719. {
  720. if (ns->discover_started || !callbacks)
  721. return (0);
  722. ns->discover_callbacks = *callbacks;
  723. ns->discover_broadcast_timeout = broadcast_timeout;
  724. if (pthread_create(&ns->discover_thread, NULL,
  725. netbios_ns_discover_thread, ns) != 0)
  726. return (0);
  727. ns->discover_started = true;
  728. return (1);
  729. }
  730. int netbios_ns_discover_stop(netbios_ns *ns)
  731. {
  732. if (ns->discover_started)
  733. {
  734. netbios_ns_abort(ns);
  735. pthread_join(ns->discover_thread, NULL);
  736. ns->discover_started = false;
  737. return (1);
  738. }
  739. else
  740. return (0);
  741. }