void io_frame_leave(struct io *io) { io_debug("io_frame_leave(%" PRIu64 ")\n", frame); if (current && current != io) errx(1, "io_frame_leave: io mismatch"); /* io has been cleared */ if (current == NULL) goto done; /* TODO: There is a possible optimization there: * In a typical half-duplex request/response scenario, * the io is waiting to read a request, and when done, it queues * the response in the output buffer and goes to write mode. * There, the write event is set and will be triggered in the next * event frame. In most case, the write call could be done * immediately as part of the last read frame, thus avoiding to go * through the event loop machinery. So, as an optimisation, we * could detect that case here and force an event dispatching. */ /* Reload the io if it has not been reset already. */ io_release(io); current = NULL; done: io_debug("=== /%" PRIu64 "\n", frame); frame += 1; }
/** b64decode Decodes a base64-encoded message. You provide an encoded message, and a pointer to somewhere we (the editorial we) can store the length of the decoded message. A dynamically allocated pointer is returned. */ char *b64decode(char *encoded, size_t *newlen) { BIO *b64, *bmem; char *decoded = NULL; if(encoded == NULL) { print_loc(); io_debug("NULL data passed! Bad coder! Bad!\n"); return NULL; } safe_malloc(decoded,*newlen); b64 = BIO_new(BIO_f_base64()); bmem = BIO_new_mem_buf(encoded, -1); if(bmem == NULL || b64 == NULL) { print_loc(); io_debug("Calls to libssl failed!\n"); abort(); //I don't think this will ever happen. } bmem = BIO_push(b64, bmem); *newlen = BIO_read(bmem, decoded, *newlen); BIO_free_all(bmem); decoded[*newlen] = '\0'; return decoded; }
void io_set_timeout(struct io *io, int msec) { io_debug("io_set_timeout(%p, %d)\n", io, msec); io->timeout = msec; }
/* * Setup the necessary events as required by the current io state, * honouring duplex mode and i/o pauses. */ void io_reload(struct io *io) { short events; /* io will be reloaded at release time */ if (io->flags & IO_HELD) return; #ifdef IO_SSL if (io->ssl) { io_reload_ssl(io); return; } #endif io_debug("io_reload(%p)\n", io); events = 0; if (IO_READING(io) && !(io->flags & IO_PAUSE_IN)) events = EV_READ; if (IO_WRITING(io) && !(io->flags & IO_PAUSE_OUT) && io_queued(io)) events |= EV_WRITE; io_reset(io, events, io_dispatch); }
/** b64encode Encodes raw data in base64. Pass the data and the length of the data. b64encode returns the base64-encoded data as a dynamically allocated char *. */ char *b64encode(char *data, size_t datalen) { BIO *b64, *bmem; BUF_MEM *bptr; char *encoded = NULL; b64 = BIO_new(BIO_f_base64()); bmem = BIO_new(BIO_s_mem()); if(bmem == NULL || b64 == NULL) { print_loc(); io_debug("Calls to libssl failed!\n"); abort(); //I don't think this will ever happen. } bmem = BIO_push(b64, bmem); BIO_write(bmem, data, datalen); BIO_flush(bmem); BIO_get_mem_ptr(bmem, &bptr); BIO_set_close(bmem, BIO_NOCLOSE); safe_malloc(encoded, bptr->length); memcpy(encoded, bptr->data, bptr->length); encoded[bptr->length] = '\0'; BIO_free_all(bmem); BUF_MEM_free(bptr); return encoded; }
/* Set the requested event. */ void io_reset(struct io *io, short events, void (*dispatch)(int, short, void*)) { struct timeval tv, *ptv; io_debug("io_reset(%p, %s, %p) -> %s\n", io, io_evstr(events), dispatch, io_strio(io)); /* * Indicate that the event has already been reset so that reload * is not called on frame_leave. */ io->flags |= IO_RESET; if (event_initialized(&io->ev)) event_del(&io->ev); /* * The io is paused by the user, so we don't want the timeout to be * effective. */ if (events == 0) return; event_set(&io->ev, io->sock, events, dispatch, io); if (io->timeout >= 0) { tv.tv_sec = io->timeout / 1000; tv.tv_usec = (io->timeout % 1000) * 1000; ptv = &tv; } else ptv = NULL; event_add(&io->ev, ptv); }
void io_set_lowat(struct io *io, size_t lowat) { io_debug("io_set_lowat(%p, %zu)\n", io, lowat); io->lowat = lowat; }
/** url2http Given a url as a string, it returns a pointer to a dynamically allocated http_request struct. args: url = A string of the following form: <scheme>://<user>:<pass>@<host>:<port>/<url-path> where <scheme> is either 'http' or 'https', and all parts are optional except the scheme, the host and the path. So, the minimum is something like <scheme>://<host>/<url-path> Returns a pointer to the http_request struct generated from the string. */ http_request *url2http(char *url) { http_request *nml; struct in_addr addr; int ssl = 0; parsed_url purl; if (url == NULL) { //print_loc(); io_debug("NULL url passed!\n"); return NULL; } if(strncmp(url, "http", 4)) { /* Must start with 'http'. */ print_loc(); io_debug("Bad URL: %s\n", url); return NULL; } purl = url_parse(url); addr = resolve_cached(purl.host, NULL, NULL); if(!strcmp(purl.scheme, "https")) ssl = 1; return new_http_req(addr, purl.port, purl.host, purl.path, ssl, purl.passwd, purl.user); }
void io_pause(struct io *io, int dir) { io_debug("io_pause(%p, %x)\n", io, dir); io->flags |= dir & (IO_PAUSE_IN | IO_PAUSE_OUT); io_reload(io); }
void io_resume(struct io *io, int dir) { io_debug("io_resume(%p, %x)\n", io, dir); io->flags &= ~(dir & (IO_PAUSE_IN | IO_PAUSE_OUT)); io_reload(io); }
/** valid_dsa_sig Given some data, the data length, the public key, the public key length, and the 48-byte signature, we verify it and return 1 for true and 0 for false, or -1 for error. */ int valid_dsa_sig(const void *data, int dlen, const char sig[48]) { int r; char hash[20]; SHA1(data, dlen, hash); r = DSA_verify(0xdecafbad, hash, 20, sig, 48, dispatch); if(!r) { print_loc(); io_err("Couldn't verify signature! Info follows:\n"); io_debug("Hexdump of signature:\n"); io_hexdump(stdout, sig, 48); io_debug("\nHexdump of data follows:\n"); io_hexdump(stdout, data, dlen); } return r; }
void io_hold(struct io *io) { io_debug("io_enter(%p)\n", io); if (io->flags & IO_HELD) errx(1, "io_hold: io is already held"); io->flags &= ~IO_RESET; io->flags |= IO_HELD; }
/* * Event framing must not rely on an io pointer to refer to the "same" io * throughout the frame, because this is not always the case: * * 1) enter(addr0) -> free(addr0) -> leave(addr0) = SEGV * 2) enter(addr0) -> free(addr0) -> malloc == addr0 -> leave(addr0) = BAD! * * In both case, the problem is that the io is freed in the callback, so * the pointer becomes invalid. If that happens, the user is required to * call io_clear, so we can adapt the frame state there. */ void io_frame_enter(const char *where, struct io *io, int ev) { io_debug("\n=== %" PRIu64 " ===\n" "io_frame_enter(%s, %s, %s)\n", frame, where, io_evstr(ev), io_strio(io)); if (current) errx(1, "io_frame_enter: interleaved frames"); current = io; io_hold(io); }
void io_set_write(struct io *io) { int mode; io_debug("io_set_write(%p)\n", io); mode = io->flags & IO_RW; if (!(mode == 0 || mode == IO_READ)) errx(1, "io_set_write(): full-duplex or writing"); io->flags &= ~IO_RW; io->flags |= IO_WRITE; io_reload(io); }
void io_dispatch_write_ssl(int fd, short event, void *humppa) { struct io *io = humppa; int n, saved_errno; size_t w2, w; io_frame_enter("io_dispatch_write_ssl", io, event); if (event == EV_TIMEOUT) { io_callback(io, IO_TIMEOUT); goto leave; } w = io_queued(io); switch ((n = iobuf_write_ssl(io->iobuf, (SSL*)io->ssl))) { case IOBUF_WANT_READ: io_reset(io, EV_READ, io_dispatch_write_ssl); break; case IOBUF_WANT_WRITE: io_reset(io, EV_WRITE, io_dispatch_write_ssl); break; case IOBUF_CLOSED: io_callback(io, IO_DISCONNECTED); break; case IOBUF_ERROR: saved_errno = errno; io->error = strerror(errno); errno = saved_errno; io_callback(io, IO_ERROR); break; case IOBUF_SSLERROR: io->error = io_ssl_error(); ssl_error("io_dispatch_write_ssl:SSL_write"); io_callback(io, IO_ERROR); break; default: io_debug("io_dispatch_write_ssl(...) -> w=%d\n", n); w2 = io_queued(io); if (w > io->lowat && w2 <= io->lowat) io_callback(io, IO_LOWAT); break; } leave: io_frame_leave(io); }
void io_dispatch_read_ssl(int fd, short event, void *humppa) { struct io *io = humppa; int n, saved_errno; io_frame_enter("io_dispatch_read_ssl", io, event); if (event == EV_TIMEOUT) { io_callback(io, IO_TIMEOUT); goto leave; } again: iobuf_normalize(&io->iobuf); switch ((n = iobuf_read_ssl(&io->iobuf, (SSL*)io->ssl))) { case IOBUF_WANT_READ: io_reset(io, EV_READ, io_dispatch_read_ssl); break; case IOBUF_WANT_WRITE: io_reset(io, EV_WRITE, io_dispatch_read_ssl); break; case IOBUF_CLOSED: io_callback(io, IO_DISCONNECTED); break; case IOBUF_ERROR: saved_errno = errno; io->error = strerror(errno); errno = saved_errno; io_callback(io, IO_ERROR); break; case IOBUF_SSLERROR: io->error = io_ssl_error(); ssl_error("io_dispatch_read_ssl:SSL_read"); io_callback(io, IO_ERROR); break; default: io_debug("io_dispatch_read_ssl(...) -> r=%d\n", n); io_callback(io, IO_DATAIN); if (current == io && IO_READING(io) && SSL_pending(io->ssl)) goto again; } leave: io_frame_leave(io); }
/** read_config Reads and parses the specified config file, and sets the appropriate options. Returns -1 on error, 0 on success. */ int read_config(const char *filename) { FILE *cf; int r; cf = fopen(filename, "r"); if(cf == NULL) { io_debug("Error opening config file '%s': %s\n" "Skipping config.\n", filename, strerror(errno)); return 0; } r = parse_config(cf); fclose(cf); parsed_yet = 1; if(r == -1) io_err("Trouble parsing %s.\n", filename); return r; }
void io_clear(struct io *io) { io_debug("io_clear(%p)\n", io); /* the current io is virtually dead */ if (io == current) current = NULL; #ifdef IO_SSL if (io->ssl) { SSL_free(io->ssl); io->ssl = NULL; } #endif if (event_initialized(&io->ev)) event_del(&io->ev); if (io->sock != -1) { close(io->sock); io->sock = -1; } }
/** save_dispatch_key Saves a base64 encoded key, as well as keeping it for future use. Pass it a key in the form of a null-terminated string. Returns -1 for error, 0 for success. */ int save_dispatch_key(const char *key) { int r; char *pub; if(dispatch != NULL) return 0; /* We have ignored you! */ dispatch = DSA_generate_parameters(DSA_KEYLEN, NULL, 0, NULL, NULL, NULL, NULL); if(dispatch == NULL) { io_debug("%s:%s():%d ", __FILE__, __func__, __LINE__); io_err("Not enough free memory! The walls are closing in!\n"); return -1; } pub = fm_abs(FM_KEY_DISPATCH_PUBLIC); r = !fm_write(FM_KEY_DISPATCH_PUBLIC, key, strlen(key)); r |= load_keys(pub, NULL, &dispatch); free(pub); return r; }