/*************************************************************************** CUdpSocket.c Network component (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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. ***************************************************************************/ #define __CUDPSOCKET_C #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 (CUDPSOCKET_Read); DECLARE_EVENT (CUDPSOCKET_SocketError); void CUdpSocket_post_data(long Param) { CUDPSOCKET *t_obj; t_obj=(CUDPSOCKET*)Param; GB.Raise(t_obj,CUDPSOCKET_Read,0); GB.Unref(POINTER(&t_obj)); } void CUdpSocket_post_error(long Param) { CUDPSOCKET *t_obj; t_obj=(CUDPSOCKET*)Param; GB.Raise(t_obj,CUDPSOCKET_SocketError,0); GB.Unref(POINTER(&t_obj)); } void CUdpSocket_CallBack(int t_sock,int type,long param) { char buf[1]; int numpoll; struct sockaddr_in t_test; unsigned int t_test_len; struct timespec mywait; CUDPSOCKET *t_obj; /* Just sleeping a little to reduce CPU waste */ mywait.tv_sec=0; mywait.tv_nsec=100000; nanosleep(&mywait,NULL); t_obj=(CUDPSOCKET*)param; if (t_obj->iStatus<=0) 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)); if (t_test.sin_port) { GB.Ref((void*)t_obj); GB.Post(CUdpSocket_post_data,(long)t_obj); } } /* 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 THIS->Socket; } int CUdpSocket_stream_close(GB_STREAM *stream) { void *_object = stream->tag; if ( !_object ) return -1; stream->desc=NULL; if (THIS->iStatus > 0) { GB.Watch (THIS->Socket,GB_WATCH_NONE,(void *)CUdpSocket_CallBack,(long)THIS); close(THIS->Socket); THIS->iStatus=0; } if (THIS->shost) GB.FreeString(&THIS->shost); if (THIS->thost) GB.FreeString(&THIS->thost); THIS->shost=NULL; THIS->thost=NULL; THIS->sport=0; THIS->tport=0; THIS->iStatus=0; return 0; } int CUdpSocket_stream_lof(GB_STREAM *stream, int64_t *len) { void *_object = stream->tag; int bytes; if ( !_object ) return -1; if (ioctl(THIS->Socket,FIONREAD,&bytes)) { CUdpSocket_stream_close(stream); THIS->iStatus=-4; return -1; } *len=bytes; return 0; } int CUdpSocket_stream_eof(GB_STREAM *stream) { void *_object = stream->tag; int bytes; if ( !_object ) return -1; if (ioctl(THIS->Socket,FIONREAD,&bytes)) { CUdpSocket_stream_close(stream); THIS->iStatus=-4; return -1; } if (!bytes) return -1; return 0; } int CUdpSocket_stream_read(GB_STREAM *stream, char *buffer, int len) { void *_object = stream->tag; int retval; int bytes=0; int NoBlock=0; unsigned int rem_host_len; struct sockaddr_in remhost; if ( !_object ) return -1; if (ioctl(THIS->Socket,FIONREAD,&bytes)) { CUdpSocket_stream_close(stream); THIS->iStatus=-4; return -1; } if (bytesSocket,FIONBIO,&NoBlock); USE_MSG_NOSIGNAL(retval=recvfrom(THIS->Socket,(void*)buffer,len*sizeof(char) \ ,MSG_NOSIGNAL,(struct sockaddr*)&remhost,&rem_host_len)); NoBlock++; ioctl(THIS->Socket,FIONBIO,&NoBlock); if (retval<0) { CUdpSocket_stream_close(&THIS->stream); THIS->iStatus=-4; return -1; } THIS->sport=ntohs(remhost.sin_port); GB.FreeString(&THIS->shost); GB.NewString (&THIS->shost , inet_ntoa(remhost.sin_addr) ,0); return 0; } int CUdpSocket_stream_write(GB_STREAM *stream, char *buffer, int len) { void *_object = stream->tag; int retval; int NoBlock=0; struct sockaddr_in remhost; struct in_addr rem_ip; if ( !_object ) return -1; if (!THIS->thost) return -1; if ( (THIS->tport<1) || (THIS->tport>65535) ) return -1; if (!inet_aton ( (const char*)THIS->thost,&rem_ip)) return -1; remhost.sin_family=AF_INET; remhost.sin_port=htons(THIS->tport); remhost.sin_addr.s_addr=rem_ip.s_addr; bzero(&(remhost.sin_zero),8); ioctl(THIS->Socket,FIONBIO,&NoBlock); USE_MSG_NOSIGNAL(retval=sendto(THIS->Socket,(void*)buffer,len*sizeof(char) \ ,MSG_NOSIGNAL,(struct sockaddr*)&remhost,sizeof(struct sockaddr))); NoBlock++; ioctl(THIS->Socket,FIONBIO,&NoBlock); if (retval>=0) return 0; CUdpSocket_stream_close(stream); THIS->iStatus= -5; return -1; } /************************************************************************************************ ################################################################################################ --------------------UDPSOCKET CLASS GAMBAS INTERFACE IMPLEMENTATION------------------------------ ################################################################################################ ***********************************************************************************************/ static bool update_broadcast(CUDPSOCKET *_object) { if (THIS->Socket == 0) return FALSE; if (setsockopt(THIS->Socket, SOL_SOCKET, SO_BROADCAST, (char *)&THIS->broadcast, sizeof(int)) < 0) { GB.Error("Cannot set broadcast socket option"); return TRUE; } else return FALSE; } static int dgram_start(CUDPSOCKET *mythis,int myport) { int NoBlock=1; struct sockaddr_in Srv; if (mythis->iStatus > 0) return 1; if ( (myport <0) || (myport>65535) ) return 8; if ( (mythis->Socket = socket(AF_INET,SOCK_DGRAM,0))<1 ) { mythis->iStatus=-2; GB.Ref(mythis); GB.Post(CUdpSocket_post_error,(long)mythis); return 2; } if (update_broadcast(mythis)) { mythis->iStatus=-2; GB.Ref(mythis); GB.Post(CUdpSocket_post_error,(long)mythis); return 2; } Srv.sin_family=AF_INET; Srv.sin_addr.s_addr=htonl(INADDR_ANY); Srv.sin_port=htons(myport); bzero(&(Srv.sin_zero),8); if ( bind (mythis->Socket,(struct sockaddr*)&Srv,sizeof(struct sockaddr)) < 0) { close (mythis->Socket); mythis->iStatus=-10; GB.Ref(mythis); GB.Post(CUdpSocket_post_error,(long)mythis); return 10; } mythis->iStatus=1; ioctl(mythis->Socket,FIONBIO,&NoBlock); GB.Watch (mythis->Socket,GB_WATCH_READ,(void *)CUdpSocket_CallBack,(long)mythis); mythis->stream.desc=&UdpSocketStream; return 0; } /********************************************************** This property gets status : 0 --> Inactive, 1 --> Working **********************************************************/ BEGIN_PROPERTY ( CUDPSOCKET_Status ) GB.ReturnInteger(THIS->iStatus); END_PROPERTY BEGIN_PROPERTY ( CUDPSOCKET_SourceHost ) GB.ReturnString(THIS->shost); END_PROPERTY BEGIN_PROPERTY ( CUDPSOCKET_SourcePort ) GB.ReturnInteger(THIS->sport); END_PROPERTY BEGIN_PROPERTY ( CUDPSOCKET_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 ( CUDPSOCKET_TargetPort ) if (READ_PROPERTY) { GB.ReturnInteger(THIS->tport); return; } if ( (VPROP(GB_INTEGER)<1) || (VPROP(GB_INTEGER)>65535) ) { GB.Error("Invalid Port value"); return; } THIS->tport=VPROP(GB_INTEGER); END_PROPERTY /************************************************* Gambas object "Constructor" *************************************************/ BEGIN_METHOD(CUDPSOCKET_new,GB_INTEGER Port;) THIS->stream.tag = _object; if (MISSING (Port) ) return; dgram_start(THIS,VARG(Port)); END_METHOD /************************************************* Gambas object "Destructor" *************************************************/ BEGIN_METHOD_VOID(CUDPSOCKET_free) CUdpSocket_stream_close(&THIS->stream); END_METHOD BEGIN_METHOD_VOID (CUDPSOCKET_Peek) char *sData=NULL; struct sockaddr_in remhost; unsigned int rem_host_len; int retval=0; int NoBlock=0; int peeking; int bytes=0; if (THIS->iStatus <= 0) { GB.Error ("Inactive"); return; } peeking=MSG_NOSIGNAL | MSG_PEEK; ioctl(THIS->Socket,FIONREAD,&bytes); if (bytes) { GB.Alloc( POINTER(&sData),bytes*sizeof(char) ); rem_host_len=sizeof(struct sockaddr); ioctl(THIS->Socket,FIONBIO,&NoBlock); USE_MSG_NOSIGNAL(retval=recvfrom(THIS->Socket,(void*)sData,1024*sizeof(char) \ ,peeking,(struct sockaddr*)&remhost,&rem_host_len)); if (retval<0) { GB.Free(POINTER(&sData)); CUdpSocket_stream_close(&THIS->stream); THIS->iStatus=-4; GB.Raise(THIS,CUDPSOCKET_SocketError,0); GB.ReturnNewString(NULL,0); return; } NoBlock++; ioctl(THIS->Socket,FIONBIO,&NoBlock); THIS->sport=ntohs(remhost.sin_port); GB.FreeString(&THIS->shost); GB.NewString ( &THIS->shost , inet_ntoa(remhost.sin_addr) ,0); if (retval>0) GB.ReturnNewString(sData,retval); else GB.ReturnNewString(NULL,0); GB.Free(POINTER(&sData)); } else { GB.FreeString(&THIS->shost); THIS->shost=NULL; THIS->sport=0; GB.ReturnNewString(NULL,0); } END_METHOD BEGIN_METHOD (CUDPSOCKET_Bind,GB_INTEGER Port;) switch( dgram_start(THIS,VARG(Port)) ) { case 1: GB.Error("Already working"); return; case 8: GB.Error("Port value is not valid"); return; } END_METHOD BEGIN_PROPERTY(CUDPSOCKET_broadcast) if (READ_PROPERTY) { GB.ReturnBoolean(THIS->broadcast); } else { THIS->broadcast = VPROP(GB_BOOLEAN); update_broadcast(THIS); } 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, &CUDPSOCKET_SocketError), GB_EVENT("Read", NULL, NULL, &CUDPSOCKET_Read), GB_METHOD("_new", NULL, CUDPSOCKET_new, "[(Port)i]"), GB_METHOD("_free", NULL, CUDPSOCKET_free, NULL), GB_METHOD("Bind", NULL, CUDPSOCKET_Bind,"(Port)i"), GB_METHOD("Peek","s",CUDPSOCKET_Peek,NULL), GB_PROPERTY_READ("Status", "i", CUDPSOCKET_Status), GB_PROPERTY_READ("SourceHost", "s", CUDPSOCKET_SourceHost), GB_PROPERTY_READ("SourcePort", "i", CUDPSOCKET_SourcePort), GB_PROPERTY("TargetHost", "s", CUDPSOCKET_TargetHost), GB_PROPERTY("TargetPort", "i", CUDPSOCKET_TargetPort), GB_PROPERTY("Broadcast", "b", CUDPSOCKET_broadcast), GB_CONSTANT("_Properties", "s", "TargetHost,TargetPort,Broadcast"), GB_CONSTANT("_DefaultEvent", "s", "Read"), GB_END_DECLARE };