/* * The QUAFFLER library * Copyright (c) 2006 Clifford Wolf * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "quaffler.h" int qfl_os_unix_listen_backlog = 20; int qfl_os_unix_connection_open(const char *peer, QFL_OS_CONNHDL_TYPE *connhdl_p, int portrange_begin, int portrange_end) { // we only support TCP connections if (strncmp(peer, "TCP:", 4)) return -1; int host_len = strcspn(peer + 4, ":"); char host[host_len+1]; memcpy(host, peer + 4, host_len); host[host_len] = 0; int port = 0; if (peer[4 + host_len] == ':') port = atoi(peer + 5 + host_len); struct sockaddr_in sin; struct hostent *hp; int fd; hp = gethostbyname(host); if (!hp) return -1; fd = socket(hp->h_addrtype, SOCK_STREAM, 0); if (fd < 0) return -1; int lport = portrange_begin; while (1) { struct sockaddr_in lin; bzero(&lin, sizeof(lin)); lin.sin_family = AF_INET; lin.sin_port = htons(lport); if (bind(fd, (struct sockaddr *)&lin, sizeof(lin)) == 0) break; if (lport > portrange_end) break; lport++; } sin.sin_family = hp->h_addrtype; bcopy(hp->h_addr, &sin.sin_addr, hp->h_length); sin.sin_port = htons(port); if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { close(fd); return -1; } fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); *connhdl_p = fd; return 0; } void qfl_os_unix_connection_close(QFL_OS_CONNHDL_TYPE *connhdl_p) { close(*connhdl_p); *connhdl_p = -1; } int qfl_os_unix_listen_open(int *port, QFL_OS_LISTENHDL_TYPE *listenhdl_p, int portrange_begin, int portrange_end) { int on = 1; struct linger sl = { 1, 5 }; struct sockaddr_in addr; int listenfd; listenfd = socket(AF_INET, SOCK_STREAM, 0); if (listenfd < 0) return -1; setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, (socklen_t)sizeof(on)); setsockopt(listenfd, SOL_SOCKET, SO_LINGER, &sl, (socklen_t)sizeof(sl)); if (*port == 0 && portrange_begin) *port = portrange_begin; bzero(&addr, sizeof(addr)); addr.sin_family=AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(*port); while (1) { if (bind(listenfd, (struct sockaddr *)&addr, sizeof(addr)) == 0) break; if (portrange_begin >= portrange_end) { close(listenfd); return -1; } *port = portrange_begin++; } if (listen(listenfd, qfl_os_unix_listen_backlog) < 0) { close(listenfd); return -1; } if (*port == 0) { socklen_t addr_len = sizeof(addr); getsockname(listenfd, (struct sockaddr *)&addr, &addr_len); *port = ntohs(addr.sin_port); } fcntl(listenfd, F_SETFL, fcntl(listenfd, F_GETFL) | O_NONBLOCK); *listenhdl_p = listenfd; return 0; } void qfl_os_unix_listen_close(QFL_OS_LISTENHDL_TYPE *listenhdl_p) { close(*listenhdl_p); *listenhdl_p = -1; } struct qfl_conn *qfl_os_unix_accept(struct qfl_ctx *ctx) { struct sockaddr_in addr; socklen_t addrlen = sizeof(addr); int connfd = accept(ctx->listenhdl, (struct sockaddr *) &addr, &addrlen); if (connfd < 0) return 0; struct qfl_conn *conn = qfl_conn_create(ctx, 0); fcntl(connfd, F_SETFL, fcntl(connfd, F_GETFL) | O_NONBLOCK); conn->connhdl = connfd; char buffer[100]; snprintf(buffer, 100, "TCP:%s:%u", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); conn->peer = strdup(buffer); return conn; } void qfl_os_unix_waitfornetwork(struct qfl_ctx *ctx, int timeout_ms) { struct timeval select_timeout; select_timeout.tv_sec = timeout_ms / 1000; select_timeout.tv_usec = (timeout_ms % 1000) * 1000; fd_set fds_read; fd_set fds_write; FD_ZERO(&fds_read); FD_ZERO(&fds_write); FD_SET(ctx->listenhdl, &fds_read); int maxfd = ctx->listenhdl; struct qfl_conn *c = ctx->conn_list; while (c) { FD_SET(c->connhdl, &fds_read); if (c->senddata) FD_SET(c->connhdl, &fds_write); if (c->connhdl > maxfd) maxfd = c->connhdl; c = c->next; } select(maxfd+1, &fds_read, &fds_write, NULL, &select_timeout); } int qfl_os_unix_send(QFL_OS_CONNHDL_TYPE *connhdl_p, const unsigned char *data, unsigned int len) { if (len <= 0) return 0; void *orig_sigpipe_handler = signal(SIGPIPE, SIG_IGN); int rc = write(*connhdl_p, data, len); signal(SIGPIPE, orig_sigpipe_handler); if (rc == 0) return -1; if (rc < 0 && errno == EAGAIN) return 0; return rc; } int qfl_os_unix_recv(QFL_OS_CONNHDL_TYPE *connhdl_p, unsigned char *data, unsigned int len) { if (len <= 0) return 0; int rc = read(*connhdl_p, data, len); if (rc == 0) return -1; if (rc < 0 && errno == EAGAIN) return 0; return rc; } int qfl_os_unix_open(QFL_OS_FILEHDL_TYPE *filehdl_p, const char *filename) { int fd = open(filename, O_RDWR|O_CREAT|O_LARGEFILE, 0666); if (fd < 0) return -1; *filehdl_p = fd; return 0; } int qfl_os_unix_open_ro(QFL_OS_FILEHDL_TYPE *filehdl_p, const char *filename) { int fd = open(filename, O_RDONLY|O_LARGEFILE); if (fd < 0) return -1; *filehdl_p = fd; return 0; } void qfl_os_unix_close(QFL_OS_FILEHDL_TYPE *filehdl_p) { close(*filehdl_p); *filehdl_p = -1; } int qfl_os_unix_test(const char *filename) { return !access(filename, R_OK|W_OK) ? 1 : 0; } void qfl_os_unix_rename(const char *old_name, const char *new_name) { assert(rename(old_name, new_name) == 0); } int qfl_os_unix_read(QFL_OS_FILEHDL_TYPE *filehdl_p, unsigned long long pos, void *data, unsigned int len) { unsigned int i = 0, j; assert(lseek64(*filehdl_p, pos, SEEK_SET) >= 0); while (i < len) { int rc = read(*filehdl_p, data+i, len-i); if (rc == 0) break; assert(rc > 0); i += rc; } for (j=i; j= 0); while (i < len) { int rc = write(*filehdl_p, data+i, len-i); assert(rc > 0); i += rc; } } long long qfl_os_unix_getsize(QFL_OS_FILEHDL_TYPE *filehdl_p) { int rc = lseek64(*filehdl_p, 0, SEEK_END); assert(rc >= 0); return rc; } void qfl_os_unix_truncate(QFL_OS_FILEHDL_TYPE *filehdl_p) { ftruncate(*filehdl_p, 0); } char *qfl_os_unix_getrandomid() { char *rc = malloc(41); int i; int fd = open("/dev/urandom", O_RDONLY); assert(fd >= 0); for (i=0; i<20; i++) { unsigned char buffer; read(fd, &buffer, 1); snprintf(rc + i*2, 3, "%02x", buffer); } close(fd); return rc; } unsigned int qfl_os_unix_rand() { static int seeded = 0; static unsigned int seed = 0; if (!seeded) { seed = getpid() << 16 | time(0); int fd = open("/dev/urandom", O_RDONLY); if (fd >= 0) { read(fd, &seed, sizeof(unsigned int)); close(fd); } seeded = 1; } return rand_r(&seed); } void qfl_os_unix_brand(void *s, int n) { static int rand_fd = -1; if (rand_fd < 0) { rand_fd = open("/dev/urandom", O_RDONLY); if (rand_fd < 0) assert(!"Can not open /dev/urandom!"); } while (n > 0) { int rc = read(rand_fd, s, n); if (rc <= 0) assert(!"I/O error while reding from /dev/urandom!"); n -= rc; s += rc; } } extern unsigned long long qfl_os_unix_time_ms() { struct timeval tv; assert(gettimeofday(&tv, 0) == 0); return (long long)tv.tv_sec * 1000 + tv.tv_usec / 1000; } char *qfl_os_unix_getconfig(const char *name) { char filename[PATH_MAX]; int fd; snprintf(filename, PATH_MAX, "%s/.quaffler/%s", getenv("HOME"), name); fd = open(filename, O_RDONLY); if (fd < 0) return 0; int len = qfl_os_unix_getsize(&fd); char *rc = malloc(len+1); assert(qfl_os_unix_read(&fd, 0, rc, len) == len); rc[len] = 0; char *nl = strchr(rc, '\n'); if (nl) *nl = 0; close(fd); return rc; } void qfl_os_unix_setconfig(const char *name, const char *value) { char filename[PATH_MAX]; int fd; snprintf(filename, PATH_MAX, "%s/.quaffler", getenv("HOME")); mkdir(filename, 0700); snprintf(filename, PATH_MAX, "%s/.quaffler/%s", getenv("HOME"), name); fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd < 0) return; int len = strlen(value); qfl_os_unix_write(&fd, 0, value, len); qfl_os_unix_write(&fd, len, "\n", 1); close(fd); }