iface_t *new_tcp_conn(int fd, iface_t *ifa) { iface_t *newifa; struct if_tcp *oift=(struct if_tcp *) ifa->info; struct if_tcp *newift=NULL; pthread_t tid; if ((newifa = malloc(sizeof(iface_t))) == NULL) return(NULL); memset(newifa,0,sizeof(iface_t)); if (((newift = malloc(sizeof(struct if_tcp))) == NULL) || ((ifa->direction != IN) && ((newifa->q=init_q(oift->qsize)) == NULL))){ if (newifa && newifa->q) free(newifa->q); if (newift) free(newift); free(newifa); return(NULL); } newift->fd=fd; newift->qsize=oift->qsize; newifa->id=ifa->id+(fd&IDMINORMASK); newifa->direction=ifa->direction; newifa->type=TCP; newifa->name=NULL; newifa->info=newift; newifa->cleanup=cleanup_tcp; newifa->write=write_tcp; newifa->read=read_tcp; newifa->lists=ifa->lists; newifa->ifilter=addfilter(ifa->ifilter); newifa->ofilter=addfilter(ifa->ofilter); newifa->checksum=ifa->checksum; if (ifa->direction == IN) newifa->q=ifa->lists->engine->q; else if (ifa->direction == BOTH) { if ((newifa->next=ifdup(newifa)) == NULL) { logwarn("Interface duplication failed"); free(newifa->q); free(newift); free(newifa); return(NULL); } newifa->direction=OUT; newifa->pair->direction=IN; newifa->pair->q=ifa->lists->engine->q; link_to_initialized(newifa->pair); pthread_create(&tid,NULL,(void *)start_interface,(void *) newifa->pair); } link_to_initialized(newifa); pthread_create(&tid,NULL,(void *)start_interface,(void *) newifa); return(newifa); }
iface_t *init_file (iface_t *ifa) { struct if_file *ifc; struct kopts *opt; struct stat statbuf; int append=0; if ((ifc = (struct if_file *)malloc(sizeof(struct if_file))) == NULL) { logerr(errno,"Could not allocate memory"); return(NULL); } memset ((void *)ifc,0,sizeof(struct if_file)); ifc->qsize=DEFFILEQSIZE; ifa->info = (void *) ifc; for(opt=ifa->options;opt;opt=opt->next) { if (!strcasecmp(opt->var,"filename")) { if (strcmp(opt->val,"-")) if ((ifc->filename=strdup(opt->val)) == NULL) { logerr(errno,"Failed to duplicate argument string"); return(NULL); } } else if (!strcasecmp(opt->var,"qsize")) { if (!(ifc->qsize=atoi(opt->val))) { logerr(0,"Invalid queue size specified: %s",opt->val); return(NULL); } } else if (!strcasecmp(opt->var,"append")) { if (!strcasecmp(opt->val,"yes")) { append++; } else if (!strcasecmp(opt->val,"no")) { append = 0; } else { logerr(0,"Invalid option \"append=%s\"",opt->val); return(NULL); } } else if (!strcasecmp(opt->var,"eol")) { if (!strcasecmp(opt->val,"rn")) ifc->usereturn=1; else if (!strcasecmp(opt->val,"n")) { ifc->usereturn=0; } else { logerr(0,"Invalid option \"eol=%s\": Must be \"n\" or \"rn\"", opt->val); return(NULL); } } else { logerr(0,"Unknown interface option %s\n",opt->var); return(NULL); } } /* We do allow use of stdin and stdout, but not if they're connected to * a terminal. This allows re-direction in background mode */ if (ifc->filename == NULL) { if (ifa->persist) { logerr(0,"Can't use persist mode with stdin/stdout"); return(NULL); } if (((ifa->direction != IN) && (((struct if_engine *)ifa->lists->engine->info)->flags & K_NOSTDOUT)) || ((ifa->direction != OUT) && (((struct if_engine *)ifa->lists->engine->info)->flags & K_NOSTDIN))) { logerr(0,"Can't use terminal stdin/stdout in background mode"); return(NULL); } ifc->fp = (ifa->direction == IN)?stdin:stdout; } else { if (ifa->direction == BOTH) { logerr(0,"Bi-directional file I/O only supported for stdin/stdout"); return(NULL); } if (stat(ifc->filename,&statbuf) < 0) { if (ifa->direction != OUT) { logerr(errno,"stat %s",ifc->filename); return(NULL); } } if (S_ISFIFO(statbuf.st_mode)) { /* Special rules for FIFOs. Opening here would hang for a reading * interface with no writer. Given that we're single threaded here, * that would be bad */ if (access(ifc->filename,(ifa->direction==IN)?R_OK:W_OK) != 0) { logerr(errno,"Could not access %s",ifc->filename); return(NULL); } } else { if (ifa->persist) { logerr(0,"Can't use persist mode on %s: Not a FIFO", ifc->filename); return(NULL); } if ((ifc->fp=fopen(ifc->filename,(ifa->direction==IN)?"r": (append)?"a":"w")) == NULL) { logerr(errno,"Failed to open %s",ifc->filename); return(NULL); } if (ifa->direction == OUT) /* Make output line buffered */ setlinebuf(ifc->fp); } } free_options(ifa->options); ifa->write=write_file; ifa->read=read_file; ifa->cleanup=cleanup_file; if (ifa->direction != IN && ifc->fp != NULL) if ((ifa->q =init_q(ifc->qsize)) == NULL) { logerr(0,"Could not create queue"); cleanup_file(ifa); return(NULL); } if (ifa->direction == BOTH) { if ((ifa->next=ifdup(ifa)) == NULL) { logerr(0,"Interface duplication failed"); cleanup_file(ifa); return(NULL); } ifa->direction=OUT; ifa->pair->direction=IN; ifc = (struct if_file *) ifa->pair->info; ifc->fp=stdin; } return(ifa); }
iface_t *init_file (iface_t *ifa) { struct if_file *ifc; struct kopts *opt; struct stat statbuf; int ret; int append=0; uid_t uid=-1; gid_t gid=-1; struct passwd *owner; struct group *group; mode_t tperm,perm=0; char *cp; if ((ifc = (struct if_file *)malloc(sizeof(struct if_file))) == NULL) { logerr(errno,"Could not allocate memory"); return(NULL); } memset ((void *)ifc,0,sizeof(struct if_file)); ifc->qsize=DEFFILEQSIZE; ifc->fd=-1; ifa->info = (void *) ifc; for(opt=ifa->options;opt;opt=opt->next) { if (!strcasecmp(opt->var,"filename")) { if (strcmp(opt->val,"-")) if ((ifc->filename=strdup(opt->val)) == NULL) { logerr(errno,"Failed to duplicate argument string"); return(NULL); } } else if (!strcasecmp(opt->var,"qsize")) { if (!(ifc->qsize=atoi(opt->val))) { logerr(0,"Invalid queue size specified: %s",opt->val); return(NULL); } } else if (!strcasecmp(opt->var,"append")) { if (!strcasecmp(opt->val,"yes")) { append++; } else if (!strcasecmp(opt->val,"no")) { append = 0; } else { logerr(0,"Invalid option \"append=%s\"",opt->val); return(NULL); } } else if (!strcasecmp(opt->var,"owner")) { if ((owner=getpwnam(opt->val)) == NULL) { logerr(0,"No such user '%s'",opt->val); return(NULL); } uid=owner->pw_uid; } else if (!strcasecmp(opt->var,"group")) { if ((group=getgrnam(opt->val)) == NULL) { logerr(0,"No such group '%s'",opt->val); return(NULL); } gid=group->gr_gid; } else if (!strcasecmp(opt->var,"perm")) { for (cp=opt->val;*cp;cp++) { if (*cp >= '0' && *cp < '8') { perm <<=3; perm += (*cp-'0'); } else { perm = 0; break; } } perm &= ACCESSPERMS; if (perm == 0) { logerr(0,"Invalid permissions for tty device \'%s\'",opt->val); return 0; } } else { logerr(0,"Unknown interface option %s\n",opt->var); return(NULL); } } /* We do allow use of stdin and stdout, but not if they're connected to * a terminal. This allows re-direction in background mode */ if (ifc->filename == NULL) { if (flag_test(ifa,F_PERSIST)) { logerr(0,"Can't use persist mode with stdin/stdout"); return(NULL); } if (((ifa->direction != IN) && (((struct if_engine *)ifa->lists->engine->info)->flags & K_NOSTDOUT)) || ((ifa->direction != OUT) && (((struct if_engine *)ifa->lists->engine->info)->flags & K_NOSTDIN))) { logerr(0,"Can't use terminal stdin/stdout in background mode"); return(NULL); } ifc->fd = (ifa->direction == IN)?STDIN_FILENO:STDOUT_FILENO; } else { if (ifa->direction == BOTH) { logerr(0,"Bi-directional file I/O only supported for stdin/stdout"); return(NULL); } if ((ret=stat(ifc->filename,&statbuf)) < 0) { if (ifa->direction != OUT) { logerr(errno,"stat %s",ifc->filename); return(NULL); } } if ((ret == 0) && S_ISFIFO(statbuf.st_mode)) { /* Special rules for FIFOs. Opening here would hang for a reading * interface with no writer. Given that we're single threaded here, * that would be bad */ if (access(ifc->filename,(ifa->direction==IN)?R_OK:W_OK) != 0) { logerr(errno,"Could not access %s",ifc->filename); return(NULL); } } else { if (flag_test(ifa,F_PERSIST)) { logerr(0,"Can't use persist mode on %s: Not a FIFO", ifc->filename); return(NULL); } if (perm) tperm=umask(0); errno=0; if (ifa->direction != IN && (ifc->fd=open(ifc->filename, O_WRONLY|O_CREAT|O_EXCL|((append)?O_APPEND:0), (perm)?perm:0664)) >= 0) { if (gid != 0 || uid != -1) { if (chown(ifc->filename,uid,gid) < 0) { logerr(errno, "Failed to set ownership or group on output file %s",ifc->filename); return(NULL); } } } else { if (errno && errno != EEXIST) { logerr(errno,"Failed to create file %s",ifc->filename); return(NULL); } if ((ifc->fd=open(ifc->filename,(ifa->direction==IN)?O_RDONLY: (O_WRONLY|((append)?O_APPEND:O_TRUNC)))) < 0) { logerr(errno,"Failed to open file %s",ifc->filename); return(NULL); } } /* reset umask: not really necessary */ if (perm) (void) umask(tperm); } } free_options(ifa->options); ifa->write=write_file; ifa->read=file_read_wrapper; ifa->readbuf=read_file; ifa->cleanup=cleanup_file; if (ifa->direction != IN && ifc->fd >= 0) if ((ifa->q =init_q(ifc->qsize)) == NULL) { logerr(0,"Could not create queue"); cleanup_file(ifa); return(NULL); } if (ifa->direction == BOTH) { if ((ifa->next=ifdup(ifa)) == NULL) { logerr(0,"Interface duplication failed"); cleanup_file(ifa); return(NULL); } ifa->direction=OUT; ifa->pair->direction=IN; ifc = (struct if_file *) ifa->pair->info; ifc->fd=STDIN_FILENO; } return(ifa); }
iface_t *init_tcp(iface_t *ifa) { struct if_tcp *ift; char *host,*port; struct addrinfo hints,*aptr,*abase; struct servent *svent; int err; int on=1,off=0; char *conntype = "c"; unsigned char *ptr; int i; struct kopts *opt; host=port=NULL; if ((ift = malloc(sizeof(struct if_tcp))) == NULL) { logerr(errno,"Could not allocate memory"); return(NULL); } ift->qsize=DEFTCPQSIZE; for(opt=ifa->options;opt;opt=opt->next) { if (!strcasecmp(opt->var,"address")) host=opt->val; else if (!strcasecmp(opt->var,"mode")) { if (strcasecmp(opt->val,"client") && strcasecmp(opt->val,"server")){ logerr(0,"Unknown tcp mode %s (must be \'client\' or \'server\')",opt->val); return(NULL); } conntype=opt->val; } else if (!strcasecmp(opt->var,"port")) { port=opt->val; } else if (!strcasecmp(opt->var,"qsize")) { if (!(ift->qsize=atoi(opt->val))) { logerr(0,"Invalid queue size specified: %s",opt->val); return(NULL); } } else { logerr(0,"unknown interface option %s\n",opt->var); return(NULL); } } if (*conntype == 'c' && !host) { logerr(0,"Must specify address for tcp client mode\n"); return(NULL); } if (!port) { if ((svent=getservbyname("nmea-0183","tcp")) != NULL) port=svent->s_name; else port = DEFTCPPORT; } memset((void *)&hints,0,sizeof(hints)); hints.ai_flags=(*conntype == 's')?AI_PASSIVE:0; hints.ai_family=AF_UNSPEC; hints.ai_socktype=SOCK_STREAM; if ((err=getaddrinfo(host,port,&hints,&abase))) { logerr(0,"Lookup failed for host %s/service %s: %s",host,port,gai_strerror(err)); return(NULL); } aptr=abase; do { if ((ift->fd=socket(aptr->ai_family,aptr->ai_socktype,aptr->ai_protocol)) < 0) continue; if (*conntype == 'c') { if (connect(ift->fd,aptr->ai_addr,aptr->ai_addrlen) == 0) break; } else { setsockopt(ift->fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); if (aptr->ai_family == AF_INET6) { for (ptr=((struct sockaddr_in6 *)aptr->ai_addr)->sin6_addr.s6_addr,i=0;i<16;i++,ptr++) if (*ptr) break; if (i == sizeof(struct in6_addr)) { if (setsockopt(ift->fd,IPPROTO_IPV6,IPV6_V6ONLY, (void *)&off,sizeof(off)) <0) { logerr(errno,"Failed to set ipv6 mapped ipv4 addresses on socket"); } } } if (bind(ift->fd,aptr->ai_addr,aptr->ai_addrlen) == 0) break; err=errno; } close(ift->fd); } while ((aptr = aptr->ai_next)); if (aptr == NULL) { logerr(err,"Failed to open tcp %s for %s/%s",(*conntype == 's')?"server":"connection",host,port); return(NULL); } ift->sa_len=aptr->ai_addrlen; (void) memcpy(&ift->sa,aptr->ai_addr,sizeof(struct sockaddr)); freeaddrinfo(abase); if ((*conntype == 'c') && (ifa->direction != IN)) { /* This is an unusual but supported combination */ if ((ifa->q =init_q(ift->qsize)) == NULL) { logerr(errno,"Interface duplication failed"); return(NULL); } } ifa->cleanup=cleanup_tcp; ifa->info = (void *) ift; if (*conntype == 'c') { ifa->read=read_tcp; ifa->write=write_tcp; if (ifa->direction == BOTH) { if ((ifa->next=ifdup(ifa)) == NULL) { logerr(errno,"Interface duplication failed"); return(NULL); } ifa->direction=OUT; ifa->pair->direction=IN; } } else { ifa->write=tcp_server; ifa->read=tcp_server; } free_options(ifa->options); return(ifa); }