/*************************************************************************** CUdpSocket.c (c) 2003-2004 Daniel Campos Fernández This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ***************************************************************************/ #define __CUDPSOCKET_C #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "tools.h" #include "CUdpSocket.h" GB_STREAM_DESC UdpSocketStream = { open: CUdpSocket_stream_open, close: CUdpSocket_stream_close, read: CUdpSocket_stream_read, write: CUdpSocket_stream_write, seek: CUdpSocket_stream_seek, tell: CUdpSocket_stream_tell, flush: CUdpSocket_stream_flush, eof: CUdpSocket_stream_eof, lof: CUdpSocket_stream_lof, handle: CUdpSocket_stream_handle }; DECLARE_EVENT (EVENT_Read); DECLARE_EVENT (EVENT_SocketError); void CUdpSocket_post_data(intptr_t Param) { CUDPSOCKET *t_obj; t_obj=(CUDPSOCKET*)Param; GB.Raise(t_obj,EVENT_Read,0); GB.Unref(POINTER(&t_obj)); } void CUdpSocket_post_error(intptr_t Param) { CUDPSOCKET *t_obj; t_obj=(CUDPSOCKET*)Param; GB.Raise(t_obj,EVENT_SocketError,0); GB.Unref(POINTER(&t_obj)); } static void clear_buffer(CUDPSOCKET *_object) { if (THIS->buffer) { GB.Free(POINTER(&THIS->buffer)); THIS->buffer_pos = 0; THIS->buffer_len = 0; } } static void fill_buffer(CUDPSOCKET *_object) { socklen_t host_len; int ret, block; char buffer[1]; //fprintf(stderr, "fill_buffer\n"); clear_buffer(THIS); host_len = sizeof(THIS->addr); block = GB.Stream.Block(&SOCKET->stream, TRUE); USE_MSG_NOSIGNAL(ret = recvfrom(SOCKET->socket, (void*)buffer, sizeof(char), MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&THIS->addr, &host_len)); GB.Stream.Block(&SOCKET->stream, block); if (ioctl(SOCKET->socket, FIONREAD, &THIS->buffer_len)) return; //fprintf(stderr, "buffer_len = %d\n", THIS->buffer_len); if (THIS->buffer_len) GB.Alloc(POINTER(&THIS->buffer), THIS->buffer_len); USE_MSG_NOSIGNAL(ret = recvfrom(SOCKET->socket, (void *)THIS->buffer, THIS->buffer_len, MSG_NOSIGNAL, (struct sockaddr*)&THIS->addr, &host_len)); //fprintf(stderr, "recvfrom() -> %d\n", ret); if (ret < 0) { CUdpSocket_stream_close(&SOCKET->stream); SOCKET->status = NET_CANNOT_READ; return; } // THIS->sport = ntohs(host.sin_port); // GB.FreeString(&THIS->shost); // GB.NewString (&THIS->shost , inet_ntoa(host.sin_addr) ,0); } void CUdpSocket_CallBack(int t_sock,int type, intptr_t param) { //struct sockaddr_in t_test; //unsigned int t_test_len; struct timespec mywait; CUDPSOCKET *_object = (CUDPSOCKET *)param; /* Just sleeping a little to reduce CPU waste */ mywait.tv_sec=0; mywait.tv_nsec=100000; nanosleep(&mywait,NULL); if (SOCKET->status <= NET_INACTIVE) return; //t_test.sin_port=0; //t_test_len=sizeof(struct sockaddr); //USE_MSG_NOSIGNAL(numpoll=recvfrom(t_sock,POINTER(buf), sizeof(char), MSG_PEEK | MSG_NOSIGNAL, (struct sockaddr*)&t_test, &t_test_len)); fill_buffer(THIS); if (THIS->buffer) { GB.Ref((void*)THIS); GB.Post(CUdpSocket_post_data, (intptr_t)THIS); } } /* not allowed methods */ int CUdpSocket_stream_open(GB_STREAM *stream, const char *path, int mode, void *data){return -1;} int CUdpSocket_stream_seek(GB_STREAM *stream, int64_t pos, int whence){return -1;} int CUdpSocket_stream_tell(GB_STREAM *stream, int64_t *pos) { *pos=0; return -1; /* not allowed */ } int CUdpSocket_stream_flush(GB_STREAM *stream) { return 0; /* OK */ } int CUdpSocket_stream_handle(GB_STREAM *stream) { void *_object = stream->tag; return SOCKET->socket; } int CUdpSocket_stream_close(GB_STREAM *stream) { void *_object = stream->tag; if ( !_object ) return -1; stream->desc=NULL; if (SOCKET->status > NET_INACTIVE) { GB.Watch (SOCKET->socket,GB_WATCH_NONE,(void *)CUdpSocket_CallBack,(intptr_t)THIS); close(SOCKET->socket); SOCKET->status = NET_INACTIVE; } GB.FreeString(&THIS->thost); GB.FreeString(&THIS->tpath); if (THIS->path) { unlink(THIS->path); GB.FreeString(&THIS->path); } THIS->tport=0; SOCKET->status = NET_INACTIVE; clear_buffer(THIS); return 0; } int CUdpSocket_stream_lof(GB_STREAM *stream, int64_t *len) { void *_object = stream->tag; *len = THIS->buffer_len - THIS->buffer_pos; return 0; } int CUdpSocket_stream_eof(GB_STREAM *stream) { void *_object = stream->tag; return THIS->buffer_pos >= THIS->buffer_len; } int CUdpSocket_stream_read(GB_STREAM *stream, char *buffer, int len) { void *_object = stream->tag; int len_max; if ( !_object ) return TRUE; len_max = THIS->buffer_len - THIS->buffer_pos; if (len_max <= 0) return TRUE; if (len > len_max) len = len_max; memcpy(buffer, &THIS->buffer[THIS->buffer_pos], len); THIS->buffer_pos += len; return len; } int CUdpSocket_stream_write(GB_STREAM *stream, char *buffer, int len) { void *_object = stream->tag; int retval; struct in_addr dest_ip; NET_ADDRESS dest; size_t size; struct sockaddr *addr; if (!THIS) return -1; CLEAR(&dest); if (THIS->tpath && *THIS->tpath) { dest.un.sun_family = PF_UNIX; strcpy(dest.un.sun_path, THIS->tpath); size = sizeof(struct sockaddr_un); addr = (struct sockaddr *)&dest.un; } else { /*if (THIS->broadcast) { fprintf(stderr, "broadcast\n"); dest.in.sin_addr.s_addr = INADDR_BROADCAST; } else*/ { if (!inet_aton((const char*)THIS->thost, &dest_ip)) return -1; dest.in.sin_addr.s_addr = dest_ip.s_addr; } dest.in.sin_family = PF_INET; dest.in.sin_port = htons(THIS->tport); size = sizeof(struct sockaddr); addr = (struct sockaddr *)&dest.in; } //fprintf(stderr, "write: %s %d %d '%.*s'\n", THIS->thost, THIS->tport, len, len, buffer); USE_MSG_NOSIGNAL(retval = sendto(SOCKET->socket, (void*)buffer, len, MSG_NOSIGNAL, addr, size)); if (retval < 0) { CUdpSocket_stream_close(stream); SOCKET->status= NET_CANNOT_WRITE; } return retval; } /************************************************************************************************ ################################################################################################ --------------------UDPSOCKET CLASS GAMBAS INTERFACE IMPLEMENTATION------------------------------ ################################################################################################ ***********************************************************************************************/ static bool update_broadcast(CUDPSOCKET *_object) { if (SOCKET->socket < 0) return FALSE; if (setsockopt(SOCKET->socket, SOL_SOCKET, SO_BROADCAST, (char *)&THIS->broadcast, sizeof(int)) < 0) { GB.Error("Cannot set broadcast socket option"); return TRUE; } else return FALSE; } static void dgram_start(CUDPSOCKET *_object) { sa_family_t domain; size_t size; struct stat info; struct sockaddr *addr; if (SOCKET->status > NET_INACTIVE) { GB.Error("Socket is active"); return; } if (THIS->path && *THIS->path) { domain = PF_UNIX; if (strlen(THIS->path) >= NET_UNIX_PATH_MAX) { GB.Error("Socket path is too long"); return; } } else { domain = PF_INET; if (THIS->port < 0 || THIS->port > 65535) { GB.Error("Invalid port number"); return; } } if ((SOCKET->socket = socket(domain, SOCK_DGRAM, 0)) < 0) { SOCKET->status = NET_CANNOT_CREATE_SOCKET; GB.Ref(THIS); GB.Post(CUdpSocket_post_error, (intptr_t)THIS); return; } if (update_broadcast(THIS) || SOCKET_update_timeout(SOCKET)) { SOCKET->status = NET_CANNOT_CREATE_SOCKET; GB.Ref(THIS); GB.Post(CUdpSocket_post_error,(intptr_t)THIS); return; } CLEAR(&THIS->addr); if (domain == PF_UNIX) { if (stat(THIS->path, &info) >= 0 && S_ISSOCK(info.st_mode)) unlink(THIS->path); THIS->addr.un.sun_family = domain; strcpy(THIS->addr.un.sun_path, THIS->path); size = sizeof(struct sockaddr_un); addr = (struct sockaddr *)&THIS->addr.un; } else { THIS->addr.in.sin_family = domain; if (!THIS->host) THIS->addr.in.sin_addr.s_addr = htonl(INADDR_ANY); else THIS->addr.in.sin_addr.s_addr = inet_addr(THIS->host); THIS->addr.in.sin_port = htons(THIS->port); size = sizeof(struct sockaddr_in); addr = (struct sockaddr *)&THIS->addr.in; //bzero(&(THIS->addr.in.sin_zero), 8); } if (bind(SOCKET->socket, addr, size) < 0) { close(SOCKET->socket); SOCKET->status = NET_CANNOT_BIND_SOCKET; GB.Ref(THIS); GB.Post(CUdpSocket_post_error, (intptr_t)THIS); return; } SOCKET->status = NET_ACTIVE; SOCKET->stream.desc = &UdpSocketStream; GB.Stream.SetSwapping(&SOCKET->stream, htons(0x1234) != 0x1234); GB.Watch(SOCKET->socket, GB_WATCH_READ, (void *)CUdpSocket_CallBack, (intptr_t)THIS); } BEGIN_PROPERTY(UdpSocket_Status) GB.ReturnInteger(SOCKET->status); END_PROPERTY BEGIN_PROPERTY(UdpSocket_SourceHost) if (THIS->addr.a.sa_family == PF_INET) GB.ReturnNewZeroString(inet_ntoa(THIS->addr.in.sin_addr)); else GB.ReturnVoidString(); END_PROPERTY BEGIN_PROPERTY(UdpSocket_SourcePort) if (THIS->addr.a.sa_family == PF_INET) GB.ReturnInteger(ntohs(THIS->addr.in.sin_port)); else GB.ReturnInteger(0); END_PROPERTY BEGIN_PROPERTY(UdpSocket_SourcePath) if (THIS->addr.a.sa_family == PF_UNIX) GB.ReturnNewZeroString(THIS->addr.un.sun_path); else GB.ReturnVoidString(); END_PROPERTY BEGIN_PROPERTY(UdpSocket_TargetHost) char *strtmp; struct in_addr rem_ip; if (READ_PROPERTY) { GB.ReturnString(THIS->thost); return; } strtmp=GB.ToZeroString(PROP(GB_STRING)); if ( !inet_aton(strtmp,&rem_ip) ) { GB.Error("Invalid IP address"); return; } GB.StoreString(PROP(GB_STRING), &THIS->thost); END_PROPERTY BEGIN_PROPERTY(UdpSocket_TargetPort) if (READ_PROPERTY) GB.ReturnInteger(THIS->tport); else { int port = VPROP(GB_INTEGER); if (port < 1 || port > 65535) { GB.Error("Invalid port number"); return; } THIS->tport = port; } END_PROPERTY BEGIN_PROPERTY(UdpSocket_TargetPath) if (READ_PROPERTY) GB.ReturnString(THIS->tpath); else { if (PLENGTH() >= NET_UNIX_PATH_MAX) { GB.Error("Socket path is too long"); return; } GB.StoreString(PROP(GB_STRING), &THIS->tpath); } END_PROPERTY /************************************************* Gambas object "Constructor" *************************************************/ BEGIN_METHOD_VOID(UdpSocket_new) SOCKET->stream.tag = _object; SOCKET->socket = -1; END_METHOD /************************************************* Gambas object "Destructor" *************************************************/ BEGIN_METHOD_VOID(UdpSocket_free) CUdpSocket_stream_close(&SOCKET->stream); GB.FreeString(&THIS->host); END_METHOD BEGIN_METHOD_VOID (UdpSocket_Peek) char *sData=NULL; socklen_t host_len; int retval=0; //int NoBlock=0; int peeking; int bytes=0; if (SOCKET->status <= NET_INACTIVE) { GB.Error("Socket is inactive"); return; } peeking = MSG_NOSIGNAL | MSG_PEEK; ioctl(SOCKET->socket,FIONREAD,&bytes); if (bytes) { GB.Alloc( POINTER(&sData),bytes*sizeof(char) ); host_len = sizeof(THIS->addr); //ioctl(SOCKET->socket,FIONBIO,&NoBlock); USE_MSG_NOSIGNAL(retval=recvfrom(SOCKET->socket, (void*)sData, 1024 * sizeof(char), peeking, (struct sockaddr*)&THIS->addr, &host_len)); if (retval<0) { GB.Free(POINTER(&sData)); CUdpSocket_stream_close(&SOCKET->stream); SOCKET->status = NET_CANNOT_READ; GB.Raise(THIS,EVENT_SocketError,0); GB.ReturnVoidString(); return; } //NoBlock++; //ioctl(SOCKET->socket,FIONBIO,&NoBlock); if (retval>0) GB.ReturnNewString(sData,retval); else GB.ReturnVoidString(); GB.Free(POINTER(&sData)); } else { GB.ReturnVoidString(); } END_METHOD BEGIN_METHOD_VOID(UdpSocket_Bind) dgram_start(THIS); END_METHOD BEGIN_PROPERTY(UdpSocket_Broadcast) if (READ_PROPERTY) { GB.ReturnBoolean(THIS->broadcast); } else { THIS->broadcast = VPROP(GB_BOOLEAN); update_broadcast(THIS); } END_PROPERTY BEGIN_PROPERTY(UdpSocket_Port) if (READ_PROPERTY) GB.ReturnInteger(THIS->port); else { int port = VPROP(GB_INTEGER); if (port < 0 || port > 65535) { GB.Error("Invalid port value"); return; } if (SOCKET->status > NET_INACTIVE) { GB.Error("Socket is active"); return; } THIS->port = port; } END_PROPERTY BEGIN_PROPERTY(UdpSocket_Path) if (READ_PROPERTY) GB.ReturnString(THIS->path); else { if (SOCKET->status > NET_INACTIVE) { GB.Error("Socket is active"); return; } GB.StoreString(PROP(GB_STRING), &THIS->path); } END_PROPERTY BEGIN_PROPERTY(UdpSocket_Host) struct in_addr rem_ip; if (READ_PROPERTY) { GB.ReturnString(THIS->host); return; } if (!inet_aton(GB.ToZeroString(PROP(GB_STRING)), &rem_ip)) { GB.Error("Invalid IP address"); return; } GB.StoreString(PROP(GB_STRING), &THIS->host); END_PROPERTY /*************************************************************** Here we declare the public interface of UdpSocket class ***************************************************************/ GB_DESC CUdpSocketDesc[] = { GB_DECLARE("UdpSocket", sizeof(CUDPSOCKET)), GB_INHERITS("Stream"), GB_EVENT("Error", NULL, NULL, &EVENT_SocketError), GB_EVENT("Read", NULL, NULL, &EVENT_Read), GB_METHOD("_new", NULL, UdpSocket_new, NULL), GB_METHOD("_free", NULL, UdpSocket_free, NULL), GB_METHOD("Bind", NULL, UdpSocket_Bind, NULL), GB_METHOD("Peek","s",UdpSocket_Peek,NULL), GB_PROPERTY_READ("Status", "i", UdpSocket_Status), GB_PROPERTY_READ("SourceHost", "s", UdpSocket_SourceHost), GB_PROPERTY_READ("SourcePort", "i", UdpSocket_SourcePort), GB_PROPERTY_READ("SourcePath", "i", UdpSocket_SourcePath), GB_PROPERTY("TargetHost", "s", UdpSocket_TargetHost), GB_PROPERTY("TargetPort", "i", UdpSocket_TargetPort), GB_PROPERTY("TargetPath", "s", UdpSocket_TargetPath), GB_PROPERTY("Host", "s", UdpSocket_Host), GB_PROPERTY("Port", "i", UdpSocket_Port), GB_PROPERTY("Path", "s", UdpSocket_Path), GB_PROPERTY("Broadcast", "b", UdpSocket_Broadcast), GB_PROPERTY("Timeout", "i", Socket_Timeout), GB_CONSTANT("_IsControl", "b", TRUE), GB_CONSTANT("_IsVirtual", "b", TRUE), GB_CONSTANT("_Group", "s", "Network"), GB_CONSTANT("_Properties", "s", "Port{Range:0;65535},Path,TargetHost,TargetPort,Broadcast"), GB_CONSTANT("_DefaultEvent", "s", "Read"), GB_END_DECLARE };