netbios_session.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /*****************************************************************************
  2. * __________________ _________ _____ _____ .__ ._.
  3. * \______ \______ \ / _____/ / \ / _ \ |__| ____ | |
  4. * | | _/| | \ \_____ \ / \ / \ / /_\ \| _/ __ \ | |
  5. * | | \| ` \/ / Y \ / | | \ ___/ \|
  6. * |______ /_______ /_______ \____|__ / /\ \____|__ |__|\___ | __
  7. * \/ \/ \/ \/ )/ \/ \/ \/
  8. *
  9. * This file is part of liBDSM. Copyright © 2014-2015 VideoLabs SAS
  10. *
  11. * Author: Julien 'Lta' BALLET <contact@lta.io>
  12. *
  13. * liBDSM is released under LGPLv2.1 (or later) and is also available
  14. * under a commercial license.
  15. *****************************************************************************
  16. * This program is free software; you can redistribute it and/or modify it
  17. * under the terms of the GNU Lesser General Public License as published by
  18. * the Free Software Foundation; either version 2.1 of the License, or
  19. * (at your option) any later version.
  20. *
  21. * This program is distributed in the hope that it will be useful,
  22. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  23. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  24. * GNU Lesser General Public License for more details.
  25. *
  26. * You should have received a copy of the GNU Lesser General Public License
  27. * along with this program; if not, write to the Free Software Foundation,
  28. * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  29. *****************************************************************************/
  30. #ifdef HAVE_CONFIG_H
  31. # include "config.h"
  32. #endif
  33. #include <assert.h>
  34. #include <stdio.h>
  35. #include <stdlib.h>
  36. #include <string.h>
  37. #include <unistd.h>
  38. #include <stdbool.h>
  39. #ifdef HAVE_ARPA_INET_H
  40. #include <arpa/inet.h>
  41. #endif
  42. #ifdef HAVE_SYS_SOCKET_H
  43. #include <sys/socket.h>
  44. #endif
  45. #include <errno.h>
  46. #include "smb_defs.h"
  47. #include "bdsm_debug.h"
  48. #include "netbios_session.h"
  49. #include "netbios_utils.h"
  50. static int open_socket_and_connect(netbios_session *s)
  51. {
  52. if ((s->socket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  53. goto error;
  54. if (connect(s->socket, (struct sockaddr *)&s->remote_addr, sizeof(s->remote_addr)) <0)
  55. goto error;
  56. return DSM_SUCCESS;
  57. error:
  58. BDSM_perror("netbios_session_new, open_socket: ");
  59. return DSM_ERROR_NETWORK;
  60. }
  61. static int session_buffer_realloc(netbios_session *s, size_t new_size)
  62. {
  63. void *new_ptr;
  64. assert(s != NULL);
  65. /* BDSM_dbg("session_buffer_realloc: from %ld bytes to %ld bytes\n", */
  66. /* s->packet_payload_size, new_size); */
  67. new_ptr = realloc(s->packet, new_size);
  68. if (new_ptr != NULL)
  69. {
  70. s->packet_payload_size = new_size;
  71. s->packet = new_ptr;
  72. return 1;
  73. }
  74. free(s->packet);
  75. s->packet = NULL;
  76. return 0;
  77. }
  78. netbios_session *netbios_session_new(size_t buf_size)
  79. {
  80. netbios_session *session;
  81. size_t packet_size;
  82. session = (netbios_session *)calloc(1, sizeof(netbios_session));
  83. if (!session)
  84. return NULL;
  85. session->packet_payload_size = buf_size;
  86. packet_size = sizeof(netbios_session_packet) + session->packet_payload_size;
  87. session->packet = (netbios_session_packet *)malloc(packet_size);
  88. if (!session->packet) {
  89. free(session);
  90. return NULL;
  91. }
  92. session->socket = -1;
  93. return session;
  94. }
  95. void netbios_session_destroy(netbios_session *s)
  96. {
  97. if (!s)
  98. return;
  99. if (s->socket != -1)
  100. closesocket(s->socket);
  101. free(s->packet);
  102. free(s);
  103. }
  104. int netbios_session_connect(uint32_t ip, netbios_session *s,
  105. const char *name, int direct_tcp)
  106. {
  107. ssize_t recv_size;
  108. char *encoded_name = NULL;
  109. uint16_t ports[2];
  110. unsigned int nb_ports;
  111. bool opened = false;
  112. assert(s != NULL && s->packet != NULL);
  113. if (direct_tcp)
  114. {
  115. ports[0] = htons(NETBIOS_PORT_DIRECT);
  116. ports[1] = htons(NETBIOS_PORT_DIRECT_SECONDARY);
  117. nb_ports = 2;
  118. }
  119. else
  120. {
  121. ports[0] = htons(NETBIOS_PORT_SESSION);
  122. nb_ports = 1;
  123. }
  124. for (unsigned int i = 0; i < nb_ports && !opened; ++i)
  125. {
  126. s->remote_addr.sin_port = ports[i];
  127. s->remote_addr.sin_family = AF_INET;
  128. s->remote_addr.sin_addr.s_addr = ip;
  129. opened = open_socket_and_connect(s) == DSM_SUCCESS;
  130. }
  131. if (!opened)
  132. goto error;
  133. if (!direct_tcp)
  134. {
  135. // Send the Session Request message
  136. netbios_session_packet_init(s);
  137. s->packet->opcode = NETBIOS_OP_SESSION_REQ;
  138. encoded_name = netbios_name_encode(name, 0, NETBIOS_FILESERVER);
  139. if (!netbios_session_packet_append(s, encoded_name, strlen(encoded_name) + 1))
  140. goto error;
  141. free(encoded_name);
  142. encoded_name = netbios_name_encode("LIBDSM", 0, NETBIOS_WORKSTATION);
  143. if (!netbios_session_packet_append(s, encoded_name, strlen(encoded_name) + 1))
  144. goto error;
  145. free(encoded_name);
  146. encoded_name = NULL;
  147. s->state = NETBIOS_SESSION_CONNECTING;
  148. if (!netbios_session_packet_send(s))
  149. goto error;
  150. // Now receiving the reply from the server.
  151. recv_size = netbios_session_packet_recv(s, NULL);
  152. if (recv_size < 0)
  153. goto error;
  154. // Reply was negative, we are not connected :(
  155. if (s->packet->opcode != NETBIOS_OP_SESSION_REQ_OK)
  156. {
  157. s->state = NETBIOS_SESSION_REFUSED;
  158. return 0;
  159. }
  160. }
  161. // Reply was OK or DirectTCP, a session has been established
  162. s->state = NETBIOS_SESSION_CONNECTED;
  163. return 1;
  164. error:
  165. free(encoded_name);
  166. s->state = NETBIOS_SESSION_ERROR;
  167. return 0;
  168. }
  169. void netbios_session_packet_init(netbios_session *s)
  170. {
  171. assert(s != NULL);
  172. s->packet_cursor = 0;
  173. s->packet->flags = 0;
  174. s->packet->opcode = NETBIOS_OP_SESSION_MSG;
  175. }
  176. int netbios_session_packet_append(netbios_session *s,
  177. const char *data, size_t size)
  178. {
  179. char *start;
  180. assert(s && s->packet);
  181. if (s->packet_payload_size - s->packet_cursor < size)
  182. if (!session_buffer_realloc(s, size + s->packet_cursor))
  183. return 0;
  184. start = ((char *)&s->packet->payload) + s->packet_cursor;
  185. memcpy(start, data, size);
  186. s->packet_cursor += size;
  187. return 1;
  188. }
  189. int netbios_session_packet_send(netbios_session *s)
  190. {
  191. ssize_t to_send;
  192. ssize_t sent;
  193. assert(s && s->packet && s->socket >= 0 && s->state > 0);
  194. s->packet->length = htons(s->packet_cursor);
  195. to_send = sizeof(netbios_session_packet) + s->packet_cursor;
  196. sent = send(s->socket, (void *)s->packet, to_send, 0);
  197. if (sent != to_send)
  198. {
  199. BDSM_perror("netbios_session_packet_send: Unable to send (full?) packet");
  200. return 0;
  201. }
  202. return sent;
  203. }
  204. static ssize_t netbios_session_get_next_packet(netbios_session *s)
  205. {
  206. ssize_t res;
  207. size_t total, sofar;
  208. assert(s != NULL && s->packet != NULL && s->socket >= 0 && s->state > 0);
  209. // Only get packet header and analyze it to get only needed number of bytes
  210. // needed for the packet. This will prevent losing a part of next packet
  211. total = sizeof(netbios_session_packet);
  212. sofar = 0;
  213. while (sofar < total)
  214. {
  215. res = recv(s->socket, (uint8_t *)(s->packet) + sofar, total - sofar, 0);
  216. if (res <= 0)
  217. {
  218. BDSM_perror("netbios_session_packet_recv: ");
  219. return -1;
  220. }
  221. sofar += res;
  222. }
  223. total = ntohs(s->packet->length);
  224. total |= (s->packet->flags & 0x01) << 16;
  225. sofar = 0;
  226. if (total + sizeof(netbios_session_packet) > s->packet_payload_size
  227. && !session_buffer_realloc(s, total + sizeof(netbios_session_packet)))
  228. return -1;
  229. //BDSM_dbg("Total = %ld, sofar = %ld\n", total, sofar);
  230. while (sofar < total)
  231. {
  232. res = recv(s->socket, (uint8_t *)(s->packet) + sizeof(netbios_session_packet)
  233. + sofar, total - sofar, 0);
  234. //BDSM_dbg("Total = %ld, sofar = %ld, res = %ld\n", total, sofar, res);
  235. if (res <= 0)
  236. {
  237. BDSM_perror("netbios_session_packet_recv: ");
  238. return -1;
  239. }
  240. sofar += res;
  241. }
  242. if (sofar > total)
  243. {
  244. BDSM_dbg("netbios_session_packet_recv: Packet size mismatch (%ld/%ld)\n",
  245. sofar, total);
  246. return -1;
  247. }
  248. return sofar;
  249. }
  250. ssize_t netbios_session_packet_recv(netbios_session *s, void **data)
  251. {
  252. ssize_t size;
  253. // ignore keepalive messages if needed
  254. do
  255. {
  256. size = netbios_session_get_next_packet(s);
  257. } while (size >= 0 && s->packet->opcode == NETBIOS_OP_SESSION_KEEPALIVE);
  258. if ((size >= 0) && (data != NULL))
  259. *data = (void *) s->packet->payload;
  260. return size;
  261. }