PageRenderTime 57ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/gambas3-3.2.0/gb.net/src/CUdpSocket.c

#
C | 648 lines | 459 code | 128 blank | 61 comment | 58 complexity | 925e85dfd2cceaf6fa0e9abf89a2aceb MD5 | raw file
Possible License(s): GPL-2.0, GPL-3.0, LGPL-2.1
  1. /***************************************************************************
  2. CUdpSocket.c
  3. (c) 2003-2004 Daniel Campos Fern??ndez <dcamposf@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  15. MA 02110-1301, USA.
  16. ***************************************************************************/
  17. #define __CUDPSOCKET_C
  18. #include <stdio.h>
  19. #include <string.h>
  20. #include <unistd.h>
  21. #include <sys/types.h>
  22. #include <sys/socket.h>
  23. #include <sys/ioctl.h>
  24. #include <sys/poll.h>
  25. #include <sys/stat.h>
  26. #include <netinet/in.h>
  27. #include <arpa/inet.h>
  28. #include <time.h>
  29. #include "main.h"
  30. #include "tools.h"
  31. #include "CUdpSocket.h"
  32. GB_STREAM_DESC UdpSocketStream = {
  33. open: CUdpSocket_stream_open,
  34. close: CUdpSocket_stream_close,
  35. read: CUdpSocket_stream_read,
  36. write: CUdpSocket_stream_write,
  37. seek: CUdpSocket_stream_seek,
  38. tell: CUdpSocket_stream_tell,
  39. flush: CUdpSocket_stream_flush,
  40. eof: CUdpSocket_stream_eof,
  41. lof: CUdpSocket_stream_lof,
  42. handle: CUdpSocket_stream_handle
  43. };
  44. DECLARE_EVENT (CUDPSOCKET_Read);
  45. DECLARE_EVENT (CUDPSOCKET_SocketError);
  46. void CUdpSocket_post_data(intptr_t Param)
  47. {
  48. CUDPSOCKET *t_obj;
  49. t_obj=(CUDPSOCKET*)Param;
  50. GB.Raise(t_obj,CUDPSOCKET_Read,0);
  51. GB.Unref(POINTER(&t_obj));
  52. }
  53. void CUdpSocket_post_error(intptr_t Param)
  54. {
  55. CUDPSOCKET *t_obj;
  56. t_obj=(CUDPSOCKET*)Param;
  57. GB.Raise(t_obj,CUDPSOCKET_SocketError,0);
  58. GB.Unref(POINTER(&t_obj));
  59. }
  60. static void clear_buffer(CUDPSOCKET *_object)
  61. {
  62. if (THIS->buffer)
  63. {
  64. GB.Free(POINTER(&THIS->buffer));
  65. THIS->buffer_pos = 0;
  66. THIS->buffer_len = 0;
  67. }
  68. }
  69. static void fill_buffer(CUDPSOCKET *_object)
  70. {
  71. socklen_t host_len;
  72. int ret, block;
  73. char buffer[1];
  74. //fprintf(stderr, "fill_buffer\n");
  75. clear_buffer(THIS);
  76. host_len = sizeof(THIS->addr);
  77. block = GB.Stream.Block(&SOCKET->stream, TRUE);
  78. USE_MSG_NOSIGNAL(ret = recvfrom(SOCKET->socket, (void*)buffer, sizeof(char), MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&THIS->addr, &host_len));
  79. GB.Stream.Block(&SOCKET->stream, block);
  80. if (ioctl(SOCKET->socket, FIONREAD, &THIS->buffer_len))
  81. return;
  82. //fprintf(stderr, "buffer_len = %d\n", THIS->buffer_len);
  83. if (THIS->buffer_len)
  84. GB.Alloc(POINTER(&THIS->buffer), THIS->buffer_len);
  85. USE_MSG_NOSIGNAL(ret = recvfrom(SOCKET->socket, (void *)THIS->buffer, THIS->buffer_len, MSG_NOSIGNAL, (struct sockaddr*)&THIS->addr, &host_len));
  86. //fprintf(stderr, "recvfrom() -> %d\n", ret);
  87. if (ret < 0)
  88. {
  89. CUdpSocket_stream_close(&SOCKET->stream);
  90. SOCKET->status = NET_CANNOT_READ;
  91. return;
  92. }
  93. // THIS->sport = ntohs(host.sin_port);
  94. // GB.FreeString(&THIS->shost);
  95. // GB.NewString (&THIS->shost , inet_ntoa(host.sin_addr) ,0);
  96. }
  97. void CUdpSocket_CallBack(int t_sock,int type, intptr_t param)
  98. {
  99. //struct sockaddr_in t_test;
  100. //unsigned int t_test_len;
  101. struct timespec mywait;
  102. CUDPSOCKET *_object = (CUDPSOCKET *)param;
  103. /* Just sleeping a little to reduce CPU waste */
  104. mywait.tv_sec=0;
  105. mywait.tv_nsec=100000;
  106. nanosleep(&mywait,NULL);
  107. if (SOCKET->status <= NET_INACTIVE) return;
  108. //t_test.sin_port=0;
  109. //t_test_len=sizeof(struct sockaddr);
  110. //USE_MSG_NOSIGNAL(numpoll=recvfrom(t_sock,POINTER(buf), sizeof(char), MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&t_test, &t_test_len));
  111. fill_buffer(THIS);
  112. if (THIS->buffer)
  113. {
  114. GB.Ref((void*)THIS);
  115. GB.Post(CUdpSocket_post_data, (intptr_t)THIS);
  116. }
  117. }
  118. /* not allowed methods */
  119. int CUdpSocket_stream_open(GB_STREAM *stream, const char *path, int mode, void *data){return -1;}
  120. int CUdpSocket_stream_seek(GB_STREAM *stream, int64_t pos, int whence){return -1;}
  121. int CUdpSocket_stream_tell(GB_STREAM *stream, int64_t *pos)
  122. {
  123. *pos=0;
  124. return -1; /* not allowed */
  125. }
  126. int CUdpSocket_stream_flush(GB_STREAM *stream)
  127. {
  128. return 0; /* OK */
  129. }
  130. int CUdpSocket_stream_handle(GB_STREAM *stream)
  131. {
  132. void *_object = stream->tag;
  133. return SOCKET->socket;
  134. }
  135. int CUdpSocket_stream_close(GB_STREAM *stream)
  136. {
  137. void *_object = stream->tag;
  138. if ( !_object ) return -1;
  139. stream->desc=NULL;
  140. if (SOCKET->status > NET_INACTIVE)
  141. {
  142. GB.Watch (SOCKET->socket,GB_WATCH_NONE,(void *)CUdpSocket_CallBack,(intptr_t)THIS);
  143. close(SOCKET->socket);
  144. SOCKET->status = NET_INACTIVE;
  145. }
  146. GB.FreeString(&THIS->thost);
  147. GB.FreeString(&THIS->tpath);
  148. if (THIS->path)
  149. {
  150. unlink(THIS->path);
  151. GB.FreeString(&THIS->path);
  152. }
  153. THIS->tport=0;
  154. SOCKET->status = NET_INACTIVE;
  155. clear_buffer(THIS);
  156. return 0;
  157. }
  158. int CUdpSocket_stream_lof(GB_STREAM *stream, int64_t *len)
  159. {
  160. void *_object = stream->tag;
  161. *len = THIS->buffer_len - THIS->buffer_pos;
  162. return 0;
  163. }
  164. int CUdpSocket_stream_eof(GB_STREAM *stream)
  165. {
  166. void *_object = stream->tag;
  167. return THIS->buffer_pos >= THIS->buffer_len;
  168. }
  169. int CUdpSocket_stream_read(GB_STREAM *stream, char *buffer, int len)
  170. {
  171. void *_object = stream->tag;
  172. int len_max;
  173. if ( !_object ) return TRUE;
  174. len_max = THIS->buffer_len - THIS->buffer_pos;
  175. if (len_max <= 0)
  176. return TRUE;
  177. if (len > len_max)
  178. len = len_max;
  179. memcpy(buffer, &THIS->buffer[THIS->buffer_pos], len);
  180. THIS->buffer_pos += len;
  181. GB.Stream.SetBytesRead(stream, len);
  182. return 0;
  183. }
  184. int CUdpSocket_stream_write(GB_STREAM *stream, char *buffer, int len)
  185. {
  186. void *_object = stream->tag;
  187. int retval;
  188. struct in_addr dest_ip;
  189. NET_ADDRESS dest;
  190. size_t size;
  191. struct sockaddr *addr;
  192. if (!THIS) return -1;
  193. CLEAR(&dest);
  194. if (THIS->tpath && *THIS->tpath)
  195. {
  196. dest.un.sun_family = PF_UNIX;
  197. strcpy(dest.un.sun_path, THIS->tpath);
  198. size = sizeof(struct sockaddr_un);
  199. addr = (struct sockaddr *)&dest.un;
  200. }
  201. else
  202. {
  203. /*if (THIS->broadcast)
  204. {
  205. fprintf(stderr, "broadcast\n");
  206. dest.in.sin_addr.s_addr = INADDR_BROADCAST;
  207. }
  208. else*/
  209. {
  210. if (!inet_aton((const char*)THIS->thost, &dest_ip))
  211. return -1;
  212. dest.in.sin_addr.s_addr = dest_ip.s_addr;
  213. }
  214. dest.in.sin_family = PF_INET;
  215. dest.in.sin_port = htons(THIS->tport);
  216. size = sizeof(struct sockaddr);
  217. addr = (struct sockaddr *)&dest.in;
  218. }
  219. //fprintf(stderr, "write: %s %d %d '%.*s'\n", THIS->thost, THIS->tport, len, len, buffer);
  220. USE_MSG_NOSIGNAL(retval = sendto(SOCKET->socket, (void*)buffer, len, MSG_NOSIGNAL, addr, size));
  221. if (retval >= 0)
  222. return 0;
  223. CUdpSocket_stream_close(stream);
  224. SOCKET->status= NET_CANNOT_WRITE;
  225. return -1;
  226. }
  227. /************************************************************************************************
  228. ################################################################################################
  229. --------------------UDPSOCKET CLASS GAMBAS INTERFACE IMPLEMENTATION------------------------------
  230. ################################################################################################
  231. ***********************************************************************************************/
  232. static bool update_broadcast(CUDPSOCKET *_object)
  233. {
  234. if (SOCKET->socket < 0)
  235. return FALSE;
  236. if (setsockopt(SOCKET->socket, SOL_SOCKET, SO_BROADCAST, (char *)&THIS->broadcast, sizeof(int)) < 0)
  237. {
  238. GB.Error("Cannot set broadcast socket option");
  239. return TRUE;
  240. }
  241. else
  242. return FALSE;
  243. }
  244. static void dgram_start(CUDPSOCKET *_object)
  245. {
  246. sa_family_t domain;
  247. size_t size;
  248. struct stat info;
  249. struct sockaddr *addr;
  250. if (SOCKET->status > NET_INACTIVE)
  251. {
  252. GB.Error("Socket is active");
  253. return;
  254. }
  255. if (THIS->path && *THIS->path)
  256. {
  257. domain = PF_UNIX;
  258. if (strlen(THIS->path) >= NET_UNIX_PATH_MAX)
  259. {
  260. GB.Error("Socket path is too long");
  261. return;
  262. }
  263. }
  264. else
  265. {
  266. domain = PF_INET;
  267. if (THIS->port < 0 || THIS->port > 65535)
  268. {
  269. GB.Error("Invalid port number");
  270. return;
  271. }
  272. }
  273. if ((SOCKET->socket = socket(domain, SOCK_DGRAM, 0)) < 0)
  274. {
  275. SOCKET->status = NET_CANNOT_CREATE_SOCKET;
  276. GB.Ref(THIS);
  277. GB.Post(CUdpSocket_post_error, (intptr_t)THIS);
  278. return;
  279. }
  280. if (update_broadcast(THIS) || SOCKET_update_timeout(SOCKET))
  281. {
  282. SOCKET->status = NET_CANNOT_CREATE_SOCKET;
  283. GB.Ref(THIS);
  284. GB.Post(CUdpSocket_post_error,(intptr_t)THIS);
  285. return;
  286. }
  287. CLEAR(&THIS->addr);
  288. if (domain == PF_UNIX)
  289. {
  290. if (stat(THIS->path, &info) >= 0 && S_ISSOCK(info.st_mode))
  291. unlink(THIS->path);
  292. THIS->addr.un.sun_family = domain;
  293. strcpy(THIS->addr.un.sun_path, THIS->path);
  294. size = sizeof(struct sockaddr_un);
  295. addr = (struct sockaddr *)&THIS->addr.un;
  296. }
  297. else
  298. {
  299. THIS->addr.in.sin_family = domain;
  300. THIS->addr.in.sin_addr.s_addr = htonl(INADDR_ANY);
  301. THIS->addr.in.sin_port = htons(THIS->port);
  302. size = sizeof(struct sockaddr_in);
  303. addr = (struct sockaddr *)&THIS->addr.in;
  304. //bzero(&(THIS->addr.in.sin_zero), 8);
  305. }
  306. if (bind(SOCKET->socket, addr, size) < 0)
  307. {
  308. close(SOCKET->socket);
  309. SOCKET->status = NET_CANNOT_BIND_SOCKET;
  310. GB.Ref(THIS);
  311. GB.Post(CUdpSocket_post_error, (intptr_t)THIS);
  312. return;
  313. }
  314. SOCKET->status = NET_ACTIVE;
  315. SOCKET->stream.desc = &UdpSocketStream;
  316. GB.Stream.SetSwapping(&SOCKET->stream, htons(0x1234) != 0x1234);
  317. GB.Watch(SOCKET->socket, GB_WATCH_READ, (void *)CUdpSocket_CallBack, (intptr_t)THIS);
  318. }
  319. BEGIN_PROPERTY(CUDPSOCKET_Status)
  320. GB.ReturnInteger(SOCKET->status);
  321. END_PROPERTY
  322. BEGIN_PROPERTY(CUDPSOCKET_SourceHost)
  323. if (THIS->addr.a.sa_family == PF_INET)
  324. GB.ReturnNewZeroString(inet_ntoa(THIS->addr.in.sin_addr));
  325. else
  326. GB.ReturnVoidString();
  327. END_PROPERTY
  328. BEGIN_PROPERTY(CUDPSOCKET_SourcePort)
  329. if (THIS->addr.a.sa_family == PF_INET)
  330. GB.ReturnInteger(ntohs(THIS->addr.in.sin_port));
  331. else
  332. GB.ReturnInteger(0);
  333. END_PROPERTY
  334. BEGIN_PROPERTY(CUDPSOCKET_SourcePath)
  335. if (THIS->addr.a.sa_family == PF_UNIX)
  336. GB.ReturnNewZeroString(THIS->addr.un.sun_path);
  337. else
  338. GB.ReturnVoidString();
  339. END_PROPERTY
  340. BEGIN_PROPERTY ( CUDPSOCKET_TargetHost )
  341. char *strtmp;
  342. struct in_addr rem_ip;
  343. if (READ_PROPERTY)
  344. {
  345. GB.ReturnString(THIS->thost);
  346. return;
  347. }
  348. strtmp=GB.ToZeroString(PROP(GB_STRING));
  349. if ( !inet_aton(strtmp,&rem_ip) )
  350. {
  351. GB.Error("Invalid IP address");
  352. return;
  353. }
  354. GB.StoreString(PROP(GB_STRING), &THIS->thost);
  355. END_PROPERTY
  356. BEGIN_PROPERTY ( CUDPSOCKET_TargetPort )
  357. if (READ_PROPERTY)
  358. GB.ReturnInteger(THIS->tport);
  359. else
  360. {
  361. int port = VPROP(GB_INTEGER);
  362. if (port < 1 || port > 65535)
  363. {
  364. GB.Error("Invalid port number");
  365. return;
  366. }
  367. THIS->tport = port;
  368. }
  369. END_PROPERTY
  370. BEGIN_PROPERTY(CUDPSOCKET_TargetPath)
  371. if (READ_PROPERTY)
  372. GB.ReturnString(THIS->tpath);
  373. else
  374. {
  375. if (PLENGTH() >= NET_UNIX_PATH_MAX)
  376. {
  377. GB.Error("Socket path is too long");
  378. return;
  379. }
  380. GB.StoreString(PROP(GB_STRING), &THIS->tpath);
  381. }
  382. END_PROPERTY
  383. /*************************************************
  384. Gambas object "Constructor"
  385. *************************************************/
  386. BEGIN_METHOD_VOID(CUDPSOCKET_new)
  387. SOCKET->stream.tag = _object;
  388. SOCKET->socket = -1;
  389. END_METHOD
  390. /*************************************************
  391. Gambas object "Destructor"
  392. *************************************************/
  393. BEGIN_METHOD_VOID(CUDPSOCKET_free)
  394. CUdpSocket_stream_close(&SOCKET->stream);
  395. END_METHOD
  396. BEGIN_METHOD_VOID (CUDPSOCKET_Peek)
  397. char *sData=NULL;
  398. socklen_t host_len;
  399. int retval=0;
  400. //int NoBlock=0;
  401. int peeking;
  402. int bytes=0;
  403. if (SOCKET->status <= NET_INACTIVE)
  404. {
  405. GB.Error("Socket is inactive");
  406. return;
  407. }
  408. peeking = MSG_NOSIGNAL | MSG_PEEK;
  409. ioctl(SOCKET->socket,FIONREAD,&bytes);
  410. if (bytes)
  411. {
  412. GB.Alloc( POINTER(&sData),bytes*sizeof(char) );
  413. host_len = sizeof(THIS->addr);
  414. //ioctl(SOCKET->socket,FIONBIO,&NoBlock);
  415. USE_MSG_NOSIGNAL(retval=recvfrom(SOCKET->socket, (void*)sData, 1024 * sizeof(char), peeking, (struct sockaddr*)&THIS->addr, &host_len));
  416. if (retval<0)
  417. {
  418. GB.Free(POINTER(&sData));
  419. CUdpSocket_stream_close(&SOCKET->stream);
  420. SOCKET->status = NET_CANNOT_READ;
  421. GB.Raise(THIS,CUDPSOCKET_SocketError,0);
  422. GB.ReturnVoidString();
  423. return;
  424. }
  425. //NoBlock++;
  426. //ioctl(SOCKET->socket,FIONBIO,&NoBlock);
  427. if (retval>0)
  428. GB.ReturnNewString(sData,retval);
  429. else
  430. GB.ReturnVoidString();
  431. GB.Free(POINTER(&sData));
  432. }
  433. else
  434. {
  435. GB.ReturnVoidString();
  436. }
  437. END_METHOD
  438. BEGIN_METHOD_VOID(CUDPSOCKET_Bind)
  439. dgram_start(THIS);
  440. END_METHOD
  441. BEGIN_PROPERTY(CUDPSOCKET_broadcast)
  442. if (READ_PROPERTY)
  443. {
  444. GB.ReturnBoolean(THIS->broadcast);
  445. }
  446. else
  447. {
  448. THIS->broadcast = VPROP(GB_BOOLEAN);
  449. update_broadcast(THIS);
  450. }
  451. END_PROPERTY
  452. BEGIN_PROPERTY(CUDPSOCKET_Port)
  453. if (READ_PROPERTY)
  454. GB.ReturnInteger(THIS->port);
  455. else
  456. {
  457. int port = VPROP(GB_INTEGER);
  458. if (port < 0 || port > 65535)
  459. {
  460. GB.Error("Invalid port value");
  461. return;
  462. }
  463. if (SOCKET->status > NET_INACTIVE)
  464. {
  465. GB.Error("Socket is active");
  466. return;
  467. }
  468. THIS->port = port;
  469. }
  470. END_PROPERTY
  471. BEGIN_PROPERTY(CUDPSOCKET_Path)
  472. if (READ_PROPERTY)
  473. GB.ReturnString(THIS->path);
  474. else
  475. {
  476. if (SOCKET->status > NET_INACTIVE)
  477. {
  478. GB.Error("Socket is active");
  479. return;
  480. }
  481. GB.StoreString(PROP(GB_STRING), &THIS->path);
  482. }
  483. END_PROPERTY
  484. /***************************************************************
  485. Here we declare the public interface of UdpSocket class
  486. ***************************************************************/
  487. GB_DESC CUdpSocketDesc[] =
  488. {
  489. GB_DECLARE("UdpSocket", sizeof(CUDPSOCKET)),
  490. GB_INHERITS("Stream"),
  491. GB_EVENT("Error", NULL, NULL, &CUDPSOCKET_SocketError),
  492. GB_EVENT("Read", NULL, NULL, &CUDPSOCKET_Read),
  493. GB_METHOD("_new", NULL, CUDPSOCKET_new, NULL),
  494. GB_METHOD("_free", NULL, CUDPSOCKET_free, NULL),
  495. GB_METHOD("Bind", NULL, CUDPSOCKET_Bind, NULL),
  496. GB_METHOD("Peek","s",CUDPSOCKET_Peek,NULL),
  497. GB_PROPERTY_READ("Status", "i", CUDPSOCKET_Status),
  498. GB_PROPERTY_READ("SourceHost", "s", CUDPSOCKET_SourceHost),
  499. GB_PROPERTY_READ("SourcePort", "i", CUDPSOCKET_SourcePort),
  500. GB_PROPERTY_READ("SourcePath", "i", CUDPSOCKET_SourcePath),
  501. GB_PROPERTY("TargetHost", "s", CUDPSOCKET_TargetHost),
  502. GB_PROPERTY("TargetPort", "i", CUDPSOCKET_TargetPort),
  503. GB_PROPERTY("TargetPath", "s", CUDPSOCKET_TargetPath),
  504. GB_PROPERTY("Port", "i", CUDPSOCKET_Port),
  505. GB_PROPERTY("Path", "s", CUDPSOCKET_Path),
  506. GB_PROPERTY("Broadcast", "b", CUDPSOCKET_broadcast),
  507. GB_PROPERTY("Timeout", "i", Socket_Timeout),
  508. GB_CONSTANT("_IsControl", "b", TRUE),
  509. GB_CONSTANT("_IsVirtual", "b", TRUE),
  510. GB_CONSTANT("_Group", "s", "Network"),
  511. GB_CONSTANT("_Properties", "s", "Port{Range:0;65535},Path,TargetHost,TargetPort,Broadcast"),
  512. GB_CONSTANT("_DefaultEvent", "s", "Read"),
  513. GB_END_DECLARE
  514. };