/* $Id$ */ /* Copyright (c) 2011 Pierre Pronchery */ /* This file is part of DeforaOS Servers inetd */ /* 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, version 3 of the License. * * 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, see . */ #include #include #include #include #include #include #include #include #include "inetd.h" #include "service.h" /* service_new */ int _service_port(char * name, char * proto); Service * service_new(char * name, ServiceSocket socket, ServiceProtocol proto, ServiceWait wait, ServiceId id, char ** program) { int port; Service * s; if((port = _service_port(name, proto == SP_TCP ? "tcp" : "udp")) == -1) return NULL; /* FIXME */ if((s = malloc(sizeof(Service))) == NULL) { inetd_error("malloc", 0); return NULL; } s->name = strdup(name); s->socket = socket; s->proto = proto; s->wait = wait; s->id = id; s->program = program; s->fd = -1; s->port = port; s->pid = -1; return s; } int _service_port(char * name, char * proto) { struct servent * se; char * p; int port; if((se = getservbyname(name, proto)) != NULL) return se->s_port; port = strtol(name, &p, 10); if(*name == '\0' || *p != '\0') return -1; return htons(port); } /* service_delete */ void service_delete(Service * s) { char ** p; free(s->name); if(s->program != NULL) { for(p = s->program; *p != NULL; p++) free(*p); free(s->program); } free(s); } /* useful */ /* service_listen */ int service_listen(Service * s) { struct sockaddr_in sa; if((s->fd = socket(AF_INET, s->socket == SS_STREAM ? SOCK_STREAM : SOCK_DGRAM, 0)) == -1) return inetd_error("socket", 1); sa.sin_family = AF_INET; sa.sin_port = s->port; sa.sin_addr.s_addr = INADDR_ANY; if(bind(s->fd, (struct sockaddr*)&sa, sizeof(sa)) != 0) { close(s->fd); s->fd = -1; return inetd_error("bind", 1); } if(s->socket == SS_STREAM && listen(s->fd, SOMAXCONN) != 0) { close(s->fd); s->fd = -1; return inetd_error("listen", 1); } if(inetd_state->debug) fprintf(stderr, "%s%s%s%d%s", "inetd: Service \"", s->name, "\" listening on port ", ntohs(s->port), "\n"); return 0; } /* service_exec */ static int _exec_tcp(Service * s); static int _exec_udp_nowait(Service * s); static int _exec_udp_wait(Service * s); int service_exec(Service * s) { switch(s->proto) { case SP_TCP: return _exec_tcp(s); case SP_UDP: if(s->wait == SW_NOWAIT) return _exec_udp_nowait(s); return _exec_udp_wait(s); default: if(inetd_state->debug) fputs("inetd: Not implemented\n", stderr); return 1; } } static int _exec_tcp(Service * s) { int fd; pid_t pid; struct sockaddr_in sa; socklen_t sa_size = sizeof(struct sockaddr_in); if((fd = accept(s->fd, (struct sockaddr*)&sa, &sa_size)) == -1) return inetd_error("accept", 1); if((pid = fork()) == -1) return inetd_error("fork", 1); else if(pid > 0) return close(fd); if(s->id.uid && setuid(s->id.uid)) inetd_error("setuid", 0); if(s->id.gid && setgid(s->id.gid)) inetd_error("setgid", 0); if(close(0) != 0 || close(1) != 0 || dup2(fd, 0) != 0 || dup2(fd, 1) != 1) inetd_error("dup2", 0); else { execv(s->program[0], &s->program[s->program[1] ? 1 : 0]); inetd_error(s->program[0], 0); } exit(2); return inetd_error("exit", 1); } static int _exec_udp_nowait(Service * s) { pid_t pid; if((pid = fork()) == -1) return inetd_error("fork", 1); else if(pid > 0) return 0; if(s->id.uid && setuid(s->id.uid)) inetd_error("setuid", 0); if(s->id.gid && setgid(s->id.gid)) inetd_error("setgid", 0); if(close(0) != 0 || close(1) != 0 || dup2(s->fd, 0) != 0 || dup2(2, 1) != 1) inetd_error("dup2", 0); execv(s->program[0], &s->program[s->program[1] ? 1 : 0]); /* FIXME * - receive packet some time (normal and error cases) */ inetd_error(s->program[0], 0); exit(2); return inetd_error("exit", 1); } static int _exec_udp_wait(Service * s) { if((s->pid = fork()) == -1) return inetd_error("fork", 1); else if(s->pid > 0) return 0; if(close(0) != 0 || close(1) != 0 || dup2(s->fd, 0) != 0 || dup2(2, 1) != 1) inetd_error("dup2", 0); else { execv(s->program[0], &s->program[s->program[1] ? 1 : 0]); inetd_error(s->program[0], 0); } exit(2); return inetd_error("exit", 1); }