#ifndef LINT static char *rcsid="$Id: serv_tcp.c,v 1.12 1999/10/03 21:18:03 crosser Exp $"; #endif /* $Log: serv_tcp.c,v $ Revision 1.12 1999/10/03 21:18:03 crosser First (probably) working version with new run time config parser. Also added listenq parameter (backlog size for listen()) and renamed wtest to whoson (and with man page). Release 2.00beta1. Revision 1.11 1999/08/30 10:20:45 crosser Fix AC_FUNC_SETPGRP Revision 1.10 1999/08/19 17:22:15 crosser Move to new config scheme (does not work yet) Revision 1.9 1998/07/28 17:51:53 crosser make 64bit architecure happy Revision 1.8 1998/07/26 14:06:40 crosser change due to checkperm names Revision 1.7 1998/07/05 00:26:18 crosser Change copyright Revision 1.6 1998/07/03 09:32:48 crosser make persistant connections work Revision 1.5 1998/07/02 18:01:15 crosser change error reporting to syslog Revision 1.4 1998/07/02 15:37:07 crosser check perms Revision 1.3 1998/07/01 08:04:58 crosser stylistic Revision 1.2 1998/07/01 05:18:09 crosser minor warnings fix Revision 1.1 1998/07/01 05:01:22 crosser Initial revision */ /* WHAT IS IT: Implementation of experimental "whoson" protocol AUTHOR: Eugene G. Crosser COPYRIGHT: Public domain */ #include "config.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_SELECT_H #include #endif #include #include #include #include #include #include #include "whosond.h" #include "rtconfig.h" #include "serv_common.h" #include "checkperm.h" #include "report.h" #include "rtc_begin.h" #include "serv_tcp_cfg.h" #include "rtc_middle.h" #include "serv_tcp_cfg.h" #include "rtc_end.h" struct _tcp_data_rec { int allow; time_t ttl; int off; char buf[512]; }; static void search_request(int fd,struct _tcp_data_rec *data) { char c,*p,*q; int crcount=0,lfcount=0; char retbuf[512]; for (p=data->buf;*p;p=q) { for (q=p;*q;q++) { switch (*q) { case '\r': crcount++; break; case '\n': lfcount++; break; default: crcount=0; lfcount=0; break; } if ((crcount > 1) || (lfcount > 1)) break; } if ((crcount > 1) || (lfcount > 1)) { while (*q && ((*q == '\r') || (*q == '\n'))) q++; c=*q; *q='\0'; retbuf[0]='\0'; if (data->allow) do_request(p,retbuf,sizeof(retbuf)); else strcpy(retbuf,NOPERM); write(fd,retbuf,strlen(retbuf)); *q=c; } else break; } if (*p) { /* move possible trailer to the start of buffer */ q=data->buf; while (*p) *q++=*p++; data->off=strlen(data->buf); } else data->off=0; } static struct _evdesc tcp_read_evproc(int fd,void *priv) { struct _tcp_data_rec *data=(struct _tcp_data_rec *)priv; struct _evdesc evdesc; int len; DPRINT(("tcp_read_evproc(%d,%p)\n",fd,priv)) memset(&evdesc,0,sizeof(struct _evdesc)); evdesc.fd=-1; if (fd < 0) { fd=-fd; close(fd); free(data); } else { if ((len=read(fd,(data->buf)+(data->off), sizeof(data->buf)-(data->off)-1)) > 0) { (data->off) += len; (data->buf)[data->off]='\0'; search_request(fd,data); evdesc.fd=fd; evdesc.evproc=tcp_read_evproc; evdesc.ttl=data->ttl; evdesc.priv=data; } if (len <= 0) { if (len < 0) ERRLOG((LOG_ERR,"read from socket: %m")) evdesc.fd=fd; close(fd); free(data); } } DPRINT(("tcp_read_evproc returns fd=%d evproc=%p\n",evdesc.fd,evdesc.evproc)) return evdesc; } static struct _evdesc tcp_ctl_evproc(int fd,void *priv) { wso_serv_tcp_cfg *rec=(wso_serv_tcp_cfg *)priv; struct _tcp_data_rec *data; struct _evdesc evdesc; struct sockaddr_in from; int flen=sizeof(from); int allow; int msgsock; DPRINT(("tcp_ctl_evproc(%d,%p)\n",fd,priv)) msgsock=accept(fd,(struct sockaddr*)&from,&flen); if (msgsock < 0) { ERRLOG((LOG_ERR,"accept: %m")) } allow=wso_perm_check(rec->perms,ntohl(from.sin_addr.s_addr)); memset(&evdesc,0,sizeof(struct _evdesc)); evdesc.fd=-1; if (msgsock >= 0) { if ((data=(struct _tcp_data_rec *)malloc (sizeof(struct _tcp_data_rec)))) { memset(data,0,sizeof(struct _tcp_data_rec)); data->allow=allow; data->ttl=rec->keepalive; evdesc.fd=msgsock; /* accept real socket */ evdesc.evproc=tcp_read_evproc; evdesc.ttl=rec->keepalive; evdesc.priv=data; } else { ERRLOG((LOG_ERR,"allocating struct _tcp_data_rec: %m")) close(msgsock); } } DPRINT(("tcp_ctl_evproc returns fd=%d evproc=%p\n",evdesc.fd,evdesc.evproc)) return evdesc; } struct _evdesc tcp_serv_init(void *priv) { wso_serv_tcp_cfg *rec=(wso_serv_tcp_cfg *)priv; struct _evdesc evdesc; struct sockaddr_in server; struct protoent *proto; int protonum; int ctlsock=-1; int tries,on=1; DPRINT(("tcp_serv_init(%p)\n",priv)) memset((char *)&server,0,sizeof(server)); server.sin_family = AF_INET; if ((proto=getprotobyname("tcp"))) protonum=proto->p_proto; else protonum=6; server.sin_port=htons(rec->port); server.sin_addr.s_addr=INADDR_ANY; if ((ctlsock=socket(AF_INET,SOCK_STREAM,protonum)) < 0) { ERRLOG((LOG_ERR,"socket: %m")) goto exit; } if (setsockopt(ctlsock,SOL_SOCKET,SO_REUSEADDR, (char*)&on,sizeof(on)) < 0) { ERRLOG((LOG_ERR,"setsockopt: %m")) ctlsock=-1; goto exit; } for (tries=0;;tries++) { if (bind(ctlsock,(struct sockaddr*)&server, sizeof(server)) < 0) { if ((errno == EADDRINUSE) && (tries < 10)) { sleep(tries); continue; } ERRLOG((LOG_ERR,"bind: %m")) ctlsock=-1; goto exit; } else break; } if (rec->listenq == 0) rec->listenq=5; if (listen(ctlsock,rec->listenq) < 0) { ERRLOG((LOG_ERR,"listen: %m")) } exit: memset(&evdesc,0,sizeof(struct _evdesc)); evdesc.fd=ctlsock; evdesc.evproc=tcp_ctl_evproc; evdesc.ttl=0; evdesc.priv=priv; DPRINT(("tcp_serv_init returns fd=%d evproc=%p\n",evdesc.fd,evdesc.evproc)) return evdesc; }