diff options
Diffstat (limited to 'dpip/dpip.c')
-rw-r--r-- | dpip/dpip.c | 366 |
1 files changed, 355 insertions, 11 deletions
diff --git a/dpip/dpip.c b/dpip/dpip.c index c6212db9..46ce6ec8 100644 --- a/dpip/dpip.c +++ b/dpip/dpip.c @@ -10,15 +10,30 @@ * */ +#include <errno.h> #include <stdio.h> +#include <stdlib.h> #include <stdarg.h> #include <string.h> +#include <ctype.h> +#include <unistd.h> /* for close */ +#include <fcntl.h> /* for fcntl */ -#include "../dlib/dlib.h" #include "dpip.h" #include "d_size.h" -static char Quote = '\''; +#define RBUF_SZ 16*1024 +//#define RBUF_SZ 1 + +#define DPIP_TAG_END " '>" +#define DPIP_MODE_SWITCH_TAG "cmd='start_send_page' " +#define MSG_ERR(...) fprintf(stderr, "[dpip]: " __VA_ARGS__) + +/* + * Local variables + */ +static const char Quote = '\''; +static const int DpipTag = 1; /* * Basically the syntax of a dpip tag is: @@ -107,17 +122,19 @@ char *a_Dpip_build_cmd(const char *format, ...) } /* - * Task: given a tag and an attribute name, return its value. - * (stuffing of ' is removed here) + * Task: given a tag, its size and an attribute name, return the + * attribute value (stuffing of ' is removed here). + * * Return value: the attribute value, or NULL if not present or malformed. */ -char *a_Dpip_get_attr(char *tag, size_t tagsize, const char *attrname) +char *a_Dpip_get_attr_l(const char *tag, size_t tagsize, const char *attrname) { uint_t i, n = 0, found = 0; - char *p, *q, *start, *val = NULL; + const char *p, *q, *start; + char *r, *s, *val = NULL; DpipTagParsingState state = SEEK_NAME; - if (!attrname || !*attrname) + if (!tag || !tagsize || !attrname || !*attrname) return NULL; for (i = 1; i < tagsize && !found; ++i) { @@ -156,13 +173,340 @@ char *a_Dpip_get_attr(char *tag, size_t tagsize, const char *attrname) p = q + 2; if (q && q[1] == ' ') { val = dStrndup(start, (uint_t)(q - start)); - for (p = q = val; (*q = *p); ++p, ++q) - if (*p == Quote && p[1] == p[0]) - ++p; + for (r = s = val; (*r = *s); ++r, ++s) + if (s[0] == Quote && s[0] == s[1]) + ++s; } } return val; } -/* ------------------------------------------------------------------------- */ +/* + * Task: given a tag and an attribute name, return its value. + * Return value: the attribute value, or NULL if not present or malformed. + */ +char *a_Dpip_get_attr(const char *tag, const char *attrname) +{ + return (tag ? a_Dpip_get_attr_l(tag, strlen(tag), attrname) : NULL); +} + +/* + * Check whether the given 'auth' string equals what dpid saved. + * Return value: 1 if equal, -1 otherwise + */ +int a_Dpip_check_auth(const char *auth_tag) +{ + char SharedSecret[32]; + FILE *In; + char *fname, *rcline = NULL, *tail, *cmd, *msg; + int i, port, ret = -1; + + /* sanity checks */ + if (!auth_tag || + !(cmd = a_Dpip_get_attr(auth_tag, "cmd")) || strcmp(cmd, "auth") || + !(msg = a_Dpip_get_attr(auth_tag, "msg"))) { + return ret; + } + + fname = dStrconcat(dGethomedir(), "/.dillo/dpid_comm_keys", NULL); + if ((In = fopen(fname, "r")) == NULL) { + MSG_ERR("[a_Dpip_check_auth] %s\n", dStrerror(errno)); + } else if ((rcline = dGetline(In)) == NULL) { + MSG_ERR("[a_Dpip_check_auth] empty file: %s\n", fname); + } else { + port = strtol(rcline, &tail, 10); + for (i = 0; *tail && isxdigit(tail[i+1]); ++i) + SharedSecret[i] = tail[i+1]; + SharedSecret[i] = 0; + if (strcmp(msg, SharedSecret) == 0) + ret = 1; + } + if (In) + fclose(In); + dFree(rcline); + dFree(fname); + dFree(msg); + dFree(cmd); + + return ret; +} + +/* -------------------------------------------------------------------------- + * Dpip socket API ---------------------------------------------------------- + */ + +/* + * Create and initialize a dpip socket handler + */ +Dsh *a_Dpip_dsh_new(int fd_in, int fd_out, int flush_sz) +{ + Dsh *dsh = dNew(Dsh, 1); + + /* init descriptors and streams */ + dsh->fd_in = fd_in; + dsh->fd_out = fd_out; + + /* init buffer */ + dsh->wrbuf = dStr_sized_new(8 *1024); + dsh->rdbuf = dStr_sized_new(8 *1024); + dsh->flush_sz = flush_sz; + dsh->mode = DPIP_TAG; + if (fcntl(dsh->fd_in, F_GETFL) & O_NONBLOCK) + dsh->mode |= DPIP_NONBLOCK; + dsh->status = 0; + + return dsh; +} + +/* + * Return value: 1..DataSize sent, -1 eagain, or -3 on big Error + */ +static int Dpip_dsh_write(Dsh *dsh, int nb, const char *Data, int DataSize) +{ + int req_mode, old_flags = 0, st, ret = -3, sent = 0; + + req_mode = (nb) ? DPIP_NONBLOCK : 0; + if ((dsh->mode & DPIP_NONBLOCK) != req_mode) { + /* change mode temporarily... */ + old_flags = fcntl(dsh->fd_out, F_GETFL); + fcntl(dsh->fd_out, F_SETFL, + (nb) ? O_NONBLOCK | old_flags : old_flags & ~O_NONBLOCK); + } + + while (1) { + st = write(dsh->fd_out, Data + sent, DataSize - sent); + if (st < 0) { + if (errno == EINTR) { + continue; + } else if (errno == EAGAIN) { + dsh->status = DPIP_EAGAIN; + ret = -1; + break; + } else { + MSG_ERR("[Dpip_dsh_write] %s\n", dStrerror(errno)); + dsh->status = DPIP_ERROR; + break; + } + } else { + sent += st; + if (nb || sent == DataSize) { + ret = sent; + break; + } + } + } + + if ((dsh->mode & DPIP_NONBLOCK) != req_mode) { + /* restore old mode */ + fcntl(dsh->fd_out, F_SETFL, old_flags); + } + + return ret; +} + +/* + * Streamed write to socket + * Return: 0 on success, 1 on error. + */ +int a_Dpip_dsh_write(Dsh *dsh, int flush, const char *Data, int DataSize) +{ + int ret = 1; + + /* append to buf */ + dStr_append_l(dsh->wrbuf, Data, DataSize); + + if (!flush || dsh->wrbuf->len == 0) + return 0; + + ret = Dpip_dsh_write(dsh, 0, dsh->wrbuf->str, dsh->wrbuf->len); + if (ret == dsh->wrbuf->len) { + dStr_truncate(dsh->wrbuf, 0); + ret = 0; + } + + return ret; +} + +/* + * Return value: 0 on success or empty buffer, + * 1..DataSize sent, -1 eagain, or -3 on big Error + */ +int a_Dpip_dsh_tryflush(Dsh *dsh) +{ + int st; + + if (dsh->wrbuf->len == 0) { + st = 0; + } else { + st = Dpip_dsh_write(dsh, 1, dsh->wrbuf->str, dsh->wrbuf->len); + if (st > 0) { + /* update internal buffer */ + dStr_erase(dsh->wrbuf, 0, st); + } + } + return (dsh->wrbuf->len == 0) ? 0 : st; +} + +/* + * Return value: 1..DataSize sent, -1 eagain, or -3 on big Error + */ +int a_Dpip_dsh_trywrite(Dsh *dsh, const char *Data, int DataSize) +{ + int st; + + if ((st = Dpip_dsh_write(dsh, 1, Data, DataSize)) > 0) { + /* update internal buffer */ + if (st < DataSize) + dStr_append_l(dsh->wrbuf, Data + st, DataSize - st); + } + return st; +} + +/* + * Convenience function. + */ +int a_Dpip_dsh_write_str(Dsh *dsh, int flush, const char *str) +{ + return a_Dpip_dsh_write(dsh, flush, str, (int)strlen(str)); +} + +/* + * Read raw data from the socket into our buffer in + * either BLOCKING or NONBLOCKING mode. + */ +static void Dpip_dsh_read(Dsh *dsh, int blocking) +{ + char buf[RBUF_SZ]; + int req_mode, old_flags = 0, st, ret = -3, nb = !blocking; + + dReturn_if (dsh->status == DPIP_ERROR || dsh->status == DPIP_EOF); + + req_mode = (nb) ? DPIP_NONBLOCK : 0; + if ((dsh->mode & DPIP_NONBLOCK) != req_mode) { + /* change mode temporarily... */ + old_flags = fcntl(dsh->fd_in, F_GETFL); + fcntl(dsh->fd_in, F_SETFL, + (nb) ? O_NONBLOCK | old_flags : old_flags & ~O_NONBLOCK); + } + + while (1) { + st = read(dsh->fd_in, buf, RBUF_SZ); + if (st < 0) { + if (errno == EINTR) { + continue; + } else if (errno == EAGAIN) { + dsh->status = DPIP_EAGAIN; + ret = -1; + break; + } else { + MSG_ERR("[Dpip_dsh_read] %s\n", dStrerror(errno)); + dsh->status = DPIP_ERROR; + break; + } + } else if (st == 0) { + dsh->status = DPIP_EOF; + break; + } else { + /* append to buf */ + dStr_append_l(dsh->rdbuf, buf, st); + if (blocking) + break; + } + } + + if ((dsh->mode & DPIP_NONBLOCK) != req_mode) { + /* restore old mode */ + fcntl(dsh->fd_out, F_SETFL, old_flags); + } + + /* assert there's no more data in the wire... + * (st < buf upon interrupt || st == buf and no more data) */ + if (blocking) + Dpip_dsh_read(dsh, 0); +} + +/* + * Return a newlly allocated string with the next dpip token in the socket. + * Return value: token string on success, NULL otherwise + */ +char *a_Dpip_dsh_read_token(Dsh *dsh, int blocking) +{ + char *p, *ret = NULL; + + /* Read all available data without blocking */ + Dpip_dsh_read(dsh, 0); + + /* switch mode upon request */ + if (dsh->mode & DPIP_LAST_TAG) + dsh->mode = DPIP_RAW; + + if (blocking) { + if (dsh->mode & DPIP_TAG) { + /* Only wait for data when the tag is incomplete */ + if (!strstr(dsh->rdbuf->str, DPIP_TAG_END)) { + do { + Dpip_dsh_read(dsh, 1); + p = strstr(dsh->rdbuf->str, DPIP_TAG_END); + } while (!p && dsh->status == EAGAIN); + } + + } else if (dsh->mode & DPIP_RAW) { + /* Wait for data when the buffer is empty and there's no EOF yet */ + while (dsh->rdbuf->len == 0 && dsh->status != DPIP_EOF) + Dpip_dsh_read(dsh, 1); + } + } + + if (dsh->mode & DPIP_TAG) { + /* return a full tag */ + if ((p = strstr(dsh->rdbuf->str, DPIP_TAG_END))) { + ret = dStrndup(dsh->rdbuf->str, p - dsh->rdbuf->str + 3); + dStr_erase(dsh->rdbuf, 0, p - dsh->rdbuf->str + 3); + if (strstr(ret, DPIP_MODE_SWITCH_TAG)) + dsh->mode |= DPIP_LAST_TAG; + } + } else { + /* raw mode, return what we have "as is" */ + if (dsh->rdbuf->len > 0) { + ret = dStrndup(dsh->rdbuf->str, dsh->rdbuf->len); + dStr_truncate(dsh->rdbuf, 0); + } + } + + return ret; +} + +/* + * Close this socket for reading and writing. + * (flush pending data) + */ +void a_Dpip_dsh_close(Dsh *dsh) +{ + int st; + + /* flush internal buffer */ + a_Dpip_dsh_write(dsh, 1, "", 0); + + /* close fds */ + while((st = close(dsh->fd_in)) < 0 && errno == EINTR) ; + if (st < 0) + MSG_ERR("[a_Dpip_dsh_close] close: %s\n", dStrerror(errno)); + if (dsh->fd_out != dsh->fd_in) { + while((st = close(dsh->fd_out)) < 0 && errno == EINTR) ; + if (st < 0) + MSG_ERR("[a_Dpip_dsh_close] close: %s\n", dStrerror(errno)); + } +} + +/* + * Free the SockHandler structure + */ +void a_Dpip_dsh_free(Dsh *dsh) +{ + dReturn_if (dsh == NULL); + + dStr_free(dsh->wrbuf, 1); + dStr_free(dsh->rdbuf, 1); + dFree(dsh); +} |