/** set_timeout check for a regex match on the -t flag (could be on argv[i] or argv[i+1]) get the timeout number convert to seconds if needed ex: to shut the server down in 4 hours: -t4h OR -t4 OR -t 4 OR -t 4h */ void set_timeout (char **argv, int *i, void *var) { int val = 0; int m = 4; /* number of parenthasized expressions being match +1 */ regex_t preg; regmatch_t pmatch[m]; if (0 != regcomp(&preg, "^(-t)?([0-9]+)([d,h,m,s])?$", REG_EXTENDED)) { dprintf (STDERR_FILENO, "Error in matcher, using default value %d for timeout\n", DEFAULT_T); *((int*)var) = DEFAULT_T; return; } if (0 == regexec(&preg, argv[(*i)], (size_t)m, pmatch, 0)) { /* flags were -tTIMEc */ val = _set_timeout (argv, *i, pmatch[2], pmatch[3]); } else if (argv[(*i)+1] && 0 == regexec(&preg, argv[(*i)+1], (size_t)m, pmatch, 0)) { /* flags were -t TIMEc */ val = _set_timeout (argv, (*i)+1, pmatch[2], pmatch[3]); } else { /* regex did not match */ val = DEFAULT_T; } regfree(&preg); _set_var_to_int(var, val, MIN_T, MAX_T, DEFAULT_T); }
int main(int argc, char *argv[]) { /* a request */ request_t request = {{0}}; /* resolving hints */ struct addrinfo hints = {0}; /* the address of a client */ struct sockaddr client_address = {0}; /* the daemon data */ daemon_t daemon_data = {{{0}}}; /* the request size */ ssize_t size = 0; /* the client address size */ socklen_t address_size = 0; /* the exit code */ int exit_code = EXIT_FAILURE; /* a received signal */ int received_signal = 0; /* child process ID */ pid_t pid = 0; /* the local address */ struct addrinfo *address = NULL; /* the file path */ const char *path = NULL; /* the transfer mode */ const char *mode = NULL; /* make sure the number of command-line arguments is valid */ if (1 != argc) { PRINT(USAGE); goto end; } /* resolve the local address */ hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG | AI_V4MAPPED; if (0 != getaddrinfo(NULL, LISTENING_PORT, &hints, &address)) { goto end; } /* create a socket */ daemon_data.fd = socket(address->ai_family, address->ai_socktype, address->ai_protocol); if (-1 == daemon_data.fd) { goto free_address; } /* set the socket timeout */ if (false == _set_timeout(daemon_data.fd)) { goto end; } /* bind the socket */ if (-1 == bind(daemon_data.fd, address->ai_addr, address->ai_addrlen)) { goto close_socket; } /* open the system log*/ openlog("tftpd", LOG_NDELAY, LOG_DAEMON); /* initialize the daemon */ if (false == daemon_init(&daemon_data, SERVER_ROOT, DAEMON_USER)) { goto close_log; } do { /* wait for a request */ if (false == daemon_wait(&daemon_data, &received_signal)) { break; } /* if the received signal is a termination one, report success */ if (SIGTERM == received_signal) { break; } /* receive the request */ address_size = sizeof(client_address); size = recvfrom(daemon_data.fd, (void *) request.data, sizeof(request.data), 0, &client_address, &address_size); switch (size) { case 0: /* if it was nothing but a scan, ignore it */ continue; case (-1): goto close_log; } /* make sure the request isn't too small */ if (MIN_REQUEST_SIZE > size) { continue; } /* make sure the request is valid */ request.header.opcode = ntohs(request.header.opcode); switch (request.header.opcode) { case PACKET_TYPE_RRQ: case PACKET_TYPE_WRQ: break; default: continue; } /* locate the path */ path = (const char *) &request.data[sizeof(uint16_t)]; /* make sure the path is null-terminated */ mode = memchr((const void *) path, '\0', 1 + NAME_MAX); if (NULL == mode) { continue; } /* skip the null byte */ ++mode; /* spawn a child process */ pid = fork(); switch (pid) { case (-1): goto close_log; case 0: /* handle the request */ if (false == _handle_request(path, mode, request.header.opcode, address, &client_address, address_size)) { goto close_log; } goto success; } } while (1); success: /* report success */ exit_code = EXIT_SUCCESS; close_log: /* close the system log */ closelog(); close_socket: /* close the socket */ (void) close(daemon_data.fd); free_address: /* free the local address */ freeaddrinfo(address); end: return exit_code; }
static bool _handle_request(const char *path, const char *mode, const uint16_t opcode, const struct addrinfo *local_address, const struct sockaddr *client_address, const socklen_t address_size) { /* the socket */ int s = (-1); /* the source address */ struct sockaddr source = {0}; /* the return value */ bool result = false; /* the transaction ID */ short tid = 0; assert(NULL != path); assert((PACKET_TYPE_RRQ == opcode) || (PACKET_TYPE_WRQ == opcode)); assert(0 < address_size); assert(NULL != local_address); assert(NULL != client_address); assert(0 < address_size); /* get the transaction ID */ switch (client_address->sa_family) { case AF_INET: tid = ((struct sockaddr_in *) client_address)->sin_port; break; case AF_INET6: tid = ((struct sockaddr_in6 *) client_address)->sin6_port; break; default: goto end; } /* create a socket */ s = socket(local_address->ai_family, local_address->ai_socktype, local_address->ai_protocol); if (-1 == s) { goto end; } /* set the socket timeout */ if (false == _set_timeout(s)) { goto end; } /* pick a unique source port and bind the socket on it */ (void) memcpy(&source, local_address->ai_addr, local_address->ai_addrlen); if (false == _pick_tid(s, &source, local_address->ai_addrlen)) { goto close_socket; } /* make sure the transfer mode is "octet" */ if (0 != strcmp("octet", mode)) { _send_error(s, ERROR_ILLEGAL_OPERATION, client_address, address_size); goto close_socket; } /* make sure the file doesn't contain a sub-director */ if (NULL != strchr(path, '/')) { _send_error(s, ERROR_ACCESS_VIOLATION, client_address, address_size); goto close_socket; } /* handle the request */ switch (opcode) { case PACKET_TYPE_RRQ: _log_request("GET", path, client_address, address_size); result = _handle_rrq(s, path, client_address, address_size, tid); break; case PACKET_TYPE_WRQ: _log_request("PUT", path, client_address, address_size); result = _handle_wrq(s, path, client_address, address_size, tid); break; default: _send_error(s, ERROR_ILLEGAL_OPERATION, client_address, address_size); goto close_socket; } close_socket: /* close the socket */ (void) close(s); end: return result; }