diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2007-03-01 23:08:32 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2007-03-01 23:08:32 +0000 |
commit | 7128853094a3df29948dc949ff04b43cccd400f7 (patch) | |
tree | 69db0978e6b835b7a736490a06a4307b1e32aa0c /gacopyz | |
parent | c6eecf2842218ed97a7084fbecae06388ec358f3 (diff) | |
download | mailfromd-7128853094a3df29948dc949ff04b43cccd400f7.tar.gz mailfromd-7128853094a3df29948dc949ff04b43cccd400f7.tar.bz2 |
Start implementing test MTA simulator and Gacopyz server code
git-svn-id: file:///svnroot/mailfromd/trunk@1269 7a8a7f39-df28-0410-adc6-e0d955640f24
Diffstat (limited to 'gacopyz')
-rw-r--r-- | gacopyz/Makefile.am | 4 | ||||
-rw-r--r-- | gacopyz/gacopyz.c | 61 | ||||
-rw-r--r-- | gacopyz/gacopyz.h | 69 | ||||
-rw-r--r-- | gacopyz/gacopyz_priv.h | 25 | ||||
-rw-r--r-- | gacopyz/io.c | 218 | ||||
-rw-r--r-- | gacopyz/log.c | 60 | ||||
-rw-r--r-- | gacopyz/server.c | 575 |
7 files changed, 973 insertions, 39 deletions
diff --git a/gacopyz/Makefile.am b/gacopyz/Makefile.am index f1aef2b0..fa793e82 100644 --- a/gacopyz/Makefile.am +++ b/gacopyz/Makefile.am @@ -1,5 +1,5 @@ # This file is part of mailfrom filter. -# Copyright (C) 2006 Sergey Poznyakoff +# Copyright (C) 2006, 2007 Sergey Poznyakoff # # 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 @@ -24,9 +24,11 @@ libgacopyz_a_SOURCES = \ gacopyz.h\ gacopyz_priv.h\ context.c\ + io.c\ log.c\ mfapi.h\ proc.c\ + server.c\ trans.h EXTRA_DIST=trans.tab trans.awk diff --git a/gacopyz/gacopyz.c b/gacopyz/gacopyz.c index ae337941..d88a2496 100644 --- a/gacopyz/gacopyz.c +++ b/gacopyz/gacopyz.c @@ -1,5 +1,5 @@ /* This file is part of gacopyz. - Copyright (C) 2006 Sergey Poznyakoff + Copyright (C) 2006, 2007 Sergey Poznyakoff 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 @@ -18,6 +18,14 @@ #include <gacopyz_priv.h> +#define TRACE(conn,cmd,size,buf) do { \ + gacopyz_log(conn, SMI_LOG_DEBUG, \ + "send header: size=%lu, cmd=%c", \ + size, cmd); \ + gacopyz_logdump(conn, SMI_LOG_DEBUG, \ + "send data", buf, size); \ +} while (0) + int gacopyz_init(gacopyz_conn_t *pconn, struct smfiDesc *desc) { @@ -63,10 +71,9 @@ gacopyz_free(gacopyz_conn_t conn) free(conn); } -static int -parse_connection(gacopyz_conn_t conn, - const char *cstr, - char **pproto, char **pport, char **ppath) +int +gacopyz_parse_connection(const char *cstr, + char **pproto, char **pport, char **ppath) { const char *p; size_t len; @@ -77,11 +84,8 @@ parse_connection(gacopyz_conn_t conn, else { len = p - cstr; *pproto = malloc(len + 1); - if (!*pproto) { - gacopyz_log(conn, SMI_LOG_ERR, - "parse_connection: not enough memory"); + if (!*pproto) return MI_FAILURE; - } memcpy(*pproto, cstr, len); (*pproto)[len] = 0; @@ -96,8 +100,6 @@ parse_connection(gacopyz_conn_t conn, *pport = malloc(len + 1); if (!*pport) { free(*pproto); - gacopyz_log(conn, SMI_LOG_ERR, - "parse_connection: not enough memory"); return MI_FAILURE; } memcpy(*pport, cstr, len); @@ -111,13 +113,25 @@ parse_connection(gacopyz_conn_t conn, free(*pproto); free(*pport); free(*ppath); - gacopyz_log(conn, SMI_LOG_ERR, - "parse_connection: not enough memory"); return MI_FAILURE; } return MI_SUCCESS; } + +static int +parse_connection(gacopyz_conn_t conn, + const char *cstr, + char **pproto, char **pport, char **ppath) +{ + int rc = gacopyz_parse_connection(cstr, pproto, pport, ppath); + if (rc) + gacopyz_log(conn, SMI_LOG_ERR, + "parse_connection: not enough memory"); + return rc; + +} + static int do_connect(gacopyz_conn_t conn, const char *cstr, char *proto, char *port, char *path, @@ -244,6 +258,11 @@ do_connect(gacopyz_conn_t conn, return -1; } } + } else { + gacopyz_log(conn, SMI_LOG_ERR, + "%s: unsupported protocol: %s", + conn->desc.xxfi_name, proto); + return -1; } fd = socket(addr.sa.sa_family, SOCK_STREAM, 0); @@ -641,7 +660,7 @@ send_reply(SMFICTX *ctx, unsigned char cmd) gacopyz_uint32_t v[3]; v[0] = htonl(ctx->conn->desc.xxfi_version); v[1] = htonl(ctx->conn->desc.xxfi_flags); - v[2] = htonl(ctx->flags); + v[2] = htonl(ctx->flags); buf = (char*) v; bufsize = sizeof v; break; @@ -651,6 +670,8 @@ send_reply(SMFICTX *ctx, unsigned char cmd) break; } + TRACE(ctx->conn, cmd, bufsize, buf); + header.hdr.size = htonl(bufsize + 1); header.hdr.cmd = cmd; rc = ctx_write(ctx, header.buf, sizeof header.buf); @@ -1416,7 +1437,7 @@ gacopyz_stop(gacopyz_conn_t conn) conn->stop = 1; return MI_SUCCESS; } - + static int gacopyz_header_command(SMFICTX *ctx, unsigned char cmd, int idx, const char *headerf, @@ -1449,6 +1470,9 @@ gacopyz_header_command(SMFICTX *ctx, unsigned char cmd, cptr += lenf; memcpy(cptr, headerv, lenv); cptr += lenv; + + TRACE(ctx->conn, cmd, bufsize, buf); + hptr->hdr.cmd = cmd; hptr->hdr.size = htonl(bufsize + 1); rc = ctx_write(ctx, buf, sizeof hptr->buf + bufsize); @@ -1474,6 +1498,9 @@ gacopyz_rcpt_command(SMFICTX *ctx, unsigned char cmd, const char *rcpt) hptr = (union header *) buf; cptr = buf + sizeof hptr->buf; strcpy(cptr, rcpt); + + TRACE(ctx->conn, cmd, bufsize, buf); + hptr->hdr.cmd = cmd; hptr->hdr.size = htonl(bufsize + 1); rc = ctx_write(ctx, buf, sizeof hptr->buf + bufsize); @@ -1554,6 +1581,9 @@ gacopyz_replace_body(SMFICTX *ctx, unsigned char *bodyp, size_t bodylen) while (bodylen) { size_t size = (bodylen >= GACOPYZ_CHUNK_SIZE) ? GACOPYZ_CHUNK_SIZE : bodylen; + + TRACE(ctx->conn, hptr->hdr.cmd, size, buf); + hptr->hdr.size = htonl(size + 1); memcpy(buf + sizeof hptr->buf, bodyp, size); if (ctx_write(ctx, buf, sizeof hptr->buf + size)) @@ -1570,6 +1600,7 @@ gacopyz_progress(SMFICTX *ctx) union header hdr; hdr.hdr.cmd = SMFIR_PROGRESS; hdr.hdr.size = htonl(1); + TRACE(ctx->conn, hdr.hdr.cmd, 0, NULL); return ctx_write(ctx, hdr.buf, sizeof hdr.buf); } diff --git a/gacopyz/gacopyz.h b/gacopyz/gacopyz.h index 2f9bb606..251810bd 100644 --- a/gacopyz/gacopyz.h +++ b/gacopyz/gacopyz.h @@ -98,10 +98,10 @@ extern "C" { #define SMFIP_NODATA 0x00000200L /* MTA should not send DATA */ /* The protocol of V1 filter */ -#define SMFI_V1_PROT SMFIP_NOCONNECT|SMFIP_NOHELO|SMFIP_NOMAIL|\ - SMFIP_NORCPT|SMFIP_NOBODY|SMFIP_NOHDRS +#define SMFI_V1_PROT (SMFIP_NOCONNECT|SMFIP_NOHELO|SMFIP_NOMAIL|\ + SMFIP_NORCPT|SMFIP_NOBODY|SMFIP_NOHDRS) /* The protocol of V2 filter */ -#define SMFI_V2_PROT SMFI_V1_PROT|SMFIP_NOEOH +#define SMFI_V2_PROT (SMFI_V1_PROT|SMFIP_NOEOH) /* Flags, defining milter capabilities */ #define SMFIF_NONE 0x00000000L /* no flags */ @@ -113,8 +113,11 @@ extern "C" { #define SMFIF_CHGHDRS 0x00000010L /* filter may change/delete headers */ #define SMFIF_QUARANTINE 0x00000020L /* filter may quarantine envelope */ -#define SMFI_V1_ACTS SMFIF_ADDHDRS|SMFIF_CHGBODY|SMFIF_ADDRCPT|SMFIF_DELRCPT -#define SMFI_V2_ACTS SMFI_V1_ACTS|SMFIF_CHGHDRS|SMFIF_QUARANTINE +#define SMFI_V1_ACTS (SMFIF_ADDHDRS|SMFIF_CHGBODY|SMFIF_ADDRCPT|SMFIF_DELRCPT) +#define SMFI_V2_ACTS (SMFI_V1_ACTS|SMFIF_CHGHDRS|SMFIF_QUARANTINE) + +#define SMFI_DEFAULT_ACTS SMFI_V2_ACTS +#define SMFI_DEFAULT_PROT SMFI_V2_PROT /* Log levels */ #define SMI_LOG_DEBUG 0 @@ -144,7 +147,8 @@ extern "C" { typedef struct smfi_str SMFICTX; typedef struct smfi_str *SMFICTX_PTR; typedef struct gacopyz_conn *gacopyz_conn_t; - +typedef struct gacopyz_iod *gacopyz_iod_t; + /* Return type for the callbacks */ typedef enum { SMFIS_CONTINUE, @@ -245,8 +249,11 @@ int gacopyz_stop(gacopyz_conn_t conn); int gacopyz_open(gacopyz_conn_t conn, const char *cstr, int backlog, int rmsocket); +int gacopyz_parse_connection(const char *cstr, + char **pproto, char **pport, char **ppath); + int gacopyz_connect(gacopyz_conn_t *, struct smfiDesc *, const char *, - int backlog, int rmsocket); + int backlog, int rmsocket); int gacopyz_get_fd(gacopyz_conn_t); int gacopyz_run(gacopyz_conn_t); @@ -288,10 +295,56 @@ void *gacopyz_getpriv (SMFICTX *ctx); size_t gacopyz_format_vbuf(char vbuf[GACOPYZ_VBUFSIZE], char *buf, size_t size); void gacopyz_log(gacopyz_conn_t conn, int level, char *fmt, ...); +void gacopyz_io_log(gacopyz_iod_t iod, int level, char *fmt, ...); void gacopyz_logmsg(int level, char *fmt, ...); +void _gacopyz_logdump(int logmask, int level, char *pfx, char *buf, + size_t size); void gacopyz_logdump(gacopyz_conn_t conn, int level, char *pfx, - char *buf, size_t size); + char *buf, size_t size); +void gacopyz_io_logdump(gacopyz_iod_t iod, int level, char *pfx, + char *buf, size_t size); void gacopyz_set_logger(void (*)(int, char *, va_list)); + +void gacopyz_stderr_log_printer(int level, char *fmt, va_list ap); +void gacopyz_syslog_log_printer(int level, char *fmt, va_list ap); + +/* Server */ +typedef struct gacopyz_srv *gacopyz_srv_t; + +int gacopyz_srv_create(gacopyz_srv_t *p, unsigned logmask); +void gacopyz_srv_destroy(gacopyz_srv_t *p); +int gacopyz_srv_find_macro (gacopyz_srv_t srv, const char *name, + const char **pval); +int gacopyz_srv_define_macro (gacopyz_srv_t srv, + const char *name, const char *value); +int gacopyz_srv_del_macro (gacopyz_srv_t srv, const char *name); +int gacopyz_srv_clear_macros (gacopyz_srv_t srv); +int gacopyz_srv_iterate_macros (gacopyz_srv_t srv, + int (*func) (const char *name, + const char *value, + void *data), + void *data); +int gacopyz_srv_count_macros (gacopyz_srv_t srv, size_t *count); +int gacopyz_srv_connect (gacopyz_srv_t srv, const char *port); +int gacopyz_srv_init(gacopyz_srv_t srv); + +int gacopyz_srv_helo (gacopyz_srv_t p, const char *domain); +int gacopyz_srv_envfrom (gacopyz_srv_t p, char **argv); +int gacopyz_srv_envrcpt (gacopyz_srv_t p, char **argv); +int gacopyz_srv_header (gacopyz_srv_t p, char *name, char *value); +int gacopyz_srv_eoh (gacopyz_srv_t p); +int gacopyz_srv_body (gacopyz_srv_t p, unsigned char *str, size_t size); + +int gacopyz_srv_eom (gacopyz_srv_t p); +int gacopyz_srv_abort (gacopyz_srv_t p); +int gacopyz_srv_close (gacopyz_srv_t p); +int gacopyz_srv_data (gacopyz_srv_t p); + +int gacopyz_srv_set_source(gacopyz_srv_t srv, unsigned long addr); +int gacopyz_srv_set_version(gacopyz_srv_t srv, int version); +int gacopyz_srv_set_protocol(gacopyz_srv_t srv, int proto); +int gacopyz_srv_set_actions(gacopyz_srv_t srv, int acts); +int gacopyz_srv_set_timeout(gacopyz_srv_t srv, time_t t); #ifdef __cplusplus } diff --git a/gacopyz/gacopyz_priv.h b/gacopyz/gacopyz_priv.h index 52bc057c..19d72468 100644 --- a/gacopyz/gacopyz_priv.h +++ b/gacopyz/gacopyz_priv.h @@ -1,5 +1,5 @@ /* This file is part of gacopyz. - Copyright (C) 2005, 2006 Sergey Poznyakoff + Copyright (C) 2005, 2006, 2007 Sergey Poznyakoff 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 @@ -37,9 +37,8 @@ typedef struct macro_assoc { char *buffer; } macro_assoc_t; -struct smfi_str -{ - gacopyz_conn_t conn; /* parent connection */ +struct smfi_str { + gacopyz_conn_t conn; /* parent connection */ int sd; /* socket descriptor */ int state; /* state; FIXME: should be enum state */ unsigned long flags; /* protocol flags */ @@ -53,7 +52,7 @@ struct gacopyz_conn { int sd; int logmask; int foreground; /* operate in foreground mode */ - int stop; /* */ + int stop; /* */ struct timeval master_timeout; struct timeval ctx_timeout; struct smfiDesc desc; @@ -69,3 +68,19 @@ typedef unsigned long gacopyz_uint32_t; #endif #define GACOPYZ_OPTLEN (sizeof(gacopyz_uint32_t) * 3) /* length of options */ + +void (*__gacopyz_log_printer)(int, char *, va_list); + + +struct gacopyz_iod { + int sd; + struct timeval timeout; + int logmask; +}; + +int _gacopyz_read (gacopyz_iod_t iod, char *buf, size_t size); +int _gacopyz_write(gacopyz_iod_t iod, char *buf, size_t size); +int gacopyz_send_command(gacopyz_iod_t iod, + int cmd, void *data, size_t size); +int gacopyz_read_command(gacopyz_iod_t iod, unsigned char *cmd, + size_t *pcount, char **pbuf, size_t *psize); diff --git a/gacopyz/io.c b/gacopyz/io.c new file mode 100644 index 00000000..29cdb390 --- /dev/null +++ b/gacopyz/io.c @@ -0,0 +1,218 @@ +/* This file is part of gacopyz. + Copyright (C) 2007 Sergey Poznyakoff + + 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; either version 2, or (at your option) + any later version. + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA */ + +#include <gacopyz_priv.h> + +int +_gacopyz_read (gacopyz_iod_t iod, char *buf, size_t size) +{ + int rc = MI_SUCCESS; + + while (size) { + fd_set rset; + fd_set xset; + int res; + struct timeval to; + + FD_ZERO(&rset); + FD_ZERO(&xset); + FD_SET(iod->sd, &rset); + FD_SET(iod->sd, &xset); + + to = iod->timeout; + + res = select(iod->sd + 1, &rset, NULL, &xset, &to); + if (res == 0) { + gacopyz_io_log(iod, + SMI_LOG_ERR, + "_gacopyz_read: connection timed out"); + errno = ETIMEDOUT; + rc = MI_FAILURE; + break; + } else if (res < 0) { + if (errno == EINTR) + continue; + rc = MI_FAILURE; + break; + } else if (rc > 0) { + if (FD_ISSET(iod->sd, &xset)) { + gacopyz_io_log(iod, + SMI_LOG_ERR, + "_gacopyz_read: " + "exception on control fd"); + rc = MI_FAILURE; + break; + } + /* Otherwise, FD_ISSET(iod->sd, &rset) is true */ + } + + res = read(iod->sd, buf, size); + if (res == -1) { + gacopyz_io_log(iod, + SMI_LOG_ERR, + "_gacopyz_read: read failed: %s", + strerror(errno)); + rc = MI_FAILURE; + break; + } else if (res == 0) { + gacopyz_io_log(iod, + SMI_LOG_ERR, + "_gacopyz_read: end of file"); + rc = MI_FAILURE; + break; + } + + buf += res; + size -= res; + } + return rc; +} + +int +_gacopyz_write(gacopyz_iod_t iod, char *buf, size_t size) +{ + int rc = MI_SUCCESS; + + while (size) { + fd_set wset; + fd_set xset; + int res; + struct timeval to; + + FD_ZERO(&wset); + FD_ZERO(&xset); + FD_SET(iod->sd, &wset); + FD_SET(iod->sd, &xset); + + to = iod->timeout; + + res = select(iod->sd + 1, NULL, &wset, &xset, &to); + if (res == 0) { + gacopyz_io_log(iod, + SMI_LOG_ERR, + "_gacopyz_write: connection timed out"); + errno = ETIMEDOUT; + rc = MI_FAILURE; + break; + } else if (res < 0) { + if (errno == EINTR) + continue; + rc = MI_FAILURE; + break; + } else if (rc > 0) { + if (FD_ISSET(iod->sd, &xset)) { + gacopyz_io_log(iod, + SMI_LOG_ERR, + "_gacopyz_write: " + "exception on control fd"); + rc = MI_FAILURE; + break; + } + /* Otherwise, FD_ISSET(iod->sd, &wset) is true */ + } + + res = write(iod->sd, buf, size); + if (res == -1) { + gacopyz_io_log(iod, + SMI_LOG_ERR, + "_gacopyz_write: write failed: %s", + strerror(errno)); + rc = MI_FAILURE; + break; + } else if (res == 0) { + gacopyz_io_log(iod, + SMI_LOG_ERR, + "_gacopyz_write: wrote 0 bytes"); + rc = MI_FAILURE; + break; + } + + buf += res; + size -= res; + } + return rc; +} + +union header { + struct { + gacopyz_uint32_t size; + unsigned char cmd; + } hdr; + char buf[5]; +}; + +int +gacopyz_send_command(gacopyz_iod_t iod, int cmd, void *data, size_t size) +{ + int rc; + union header header; + + gacopyz_io_log(iod, SMI_LOG_DEBUG, + "send header: size=%lu, cmd=%c", + size, cmd); + + gacopyz_io_logdump(iod, SMI_LOG_DEBUG, + "send data", data, size); + header.hdr.size = htonl(size + 1); + header.hdr.cmd = cmd; + rc = _gacopyz_write(iod, header.buf, sizeof header.buf); + if (rc != MI_SUCCESS) + return rc; + if (size) + rc = _gacopyz_write(iod, data, size); + return rc; +} + +int +gacopyz_read_command(gacopyz_iod_t iod, unsigned char *cmd, size_t *pcount, + char **pbuf, size_t *psize) +{ + union header header; + size_t size; + int rc; + + if ((rc = _gacopyz_read(iod, header.buf, sizeof header.buf)) + != MI_SUCCESS) + return rc; + + size = ntohl(header.hdr.size) - 1; + if (size + 1 > *psize) { + char *p = realloc(*pbuf, size + 1); + if (!p) { + gacopyz_io_log(iod, + SMI_LOG_ERR, "not enough memory"); + return MI_FAILURE; + } + *pbuf = p; + *psize = size + 1; + } + + gacopyz_io_log(iod, SMI_LOG_DEBUG, + "read header: size=%lu, cmd=%c", + size, header.hdr.cmd); + + if ((rc = _gacopyz_read(iod, *pbuf, size)) != MI_SUCCESS) + return rc; + (*pbuf)[size] = 0; + + gacopyz_io_logdump(iod, SMI_LOG_DEBUG, + "read data", *pbuf, size); + *pcount = size; + *cmd = header.hdr.cmd; + return MI_SUCCESS; +} diff --git a/gacopyz/log.c b/gacopyz/log.c index 2d835d09..43eae63e 100644 --- a/gacopyz/log.c +++ b/gacopyz/log.c @@ -18,12 +18,12 @@ #include <gacopyz_priv.h> -static void default__smfi_log_printer(int, char *, va_list); -static void (*__smfi_log_printer)(int, char *, va_list) = default__smfi_log_printer; +void (*__gacopyz_log_printer)(int, char *, va_list) = + gacopyz_syslog_log_printer; /* Logging */ -static void -default__smfi_log_printer(int level, char *fmt, va_list ap) +void +gacopyz_syslog_log_printer(int level, char *fmt, va_list ap) { switch (level) { case SMI_LOG_DEBUG: @@ -46,12 +46,29 @@ default__smfi_log_printer(int level, char *fmt, va_list ap) vsyslog(level, fmt, ap); /* FIXME: if absent? */ } +static char *level_name[] = { + "DEBUG", + "INFO", + "WARN", + "ERR", + "FATAL" +}; + +void +gacopyz_stderr_log_printer(int level, char *fmt, va_list ap) +{ + fprintf(stderr, "Gacopyz %s: ", level_name[level]); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); +} + + void gacopyz_logmsg(int level, char *fmt, ...) { va_list ap; va_start(ap, fmt); - __smfi_log_printer(level, fmt, ap); + __gacopyz_log_printer(level, fmt, ap); va_end(ap); } @@ -61,7 +78,18 @@ gacopyz_log(gacopyz_conn_t conn, int level, char *fmt, ...) if (conn->logmask & SMI_LOG_MASK(level)) { va_list ap; va_start(ap, fmt); - __smfi_log_printer(level, fmt, ap); + __gacopyz_log_printer(level, fmt, ap); + va_end(ap); + } +} + +void +gacopyz_io_log(gacopyz_iod_t iod, int level, char *fmt, ...) +{ + if (iod->logmask & SMI_LOG_MASK(level)) { + va_list ap; + va_start(ap, fmt); + __gacopyz_log_printer(level, fmt, ap); va_end(ap); } } @@ -96,10 +124,9 @@ gacopyz_format_vbuf(char vbuf[GACOPYZ_VBUFSIZE], char *buf, size_t size) } void -gacopyz_logdump(gacopyz_conn_t conn, int level, char *pfx, - char *buf, size_t size) +_gacopyz_logdump(int logmask, int level, char *pfx, char *buf, size_t size) { - if (conn->logmask & SMI_LOG_MASK(level)) { + if (logmask & SMI_LOG_MASK(level)) { char vbuf[GACOPYZ_VBUFSIZE]; while (size) { @@ -111,9 +138,22 @@ gacopyz_logdump(gacopyz_conn_t conn, int level, char *pfx, } } +void +gacopyz_logdump(gacopyz_conn_t conn, int level, char *pfx, + char *buf, size_t size) +{ + return _gacopyz_logdump(conn->logmask, level, pfx, buf, size); +} + +void +gacopyz_io_logdump(gacopyz_iod_t iod, int level, char *pfx, + char *buf, size_t size) +{ + return _gacopyz_logdump(iod->logmask, level, pfx, buf, size); +} void gacopyz_set_logger(void (*logger)(int, char *, va_list)) { - __smfi_log_printer = logger; + __gacopyz_log_printer = logger; } diff --git a/gacopyz/server.c b/gacopyz/server.c new file mode 100644 index 00000000..944952a3 --- /dev/null +++ b/gacopyz/server.c @@ -0,0 +1,575 @@ +/* This file is part of gacopyz. + Copyright (C) 2007 Sergey Poznyakoff + + 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; either version 2, or (at your option) + any later version. + + 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, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA */ + +#include <gacopyz_priv.h> + +typedef struct gacopyz_macro_def *gacopyz_macro_def_t; +struct gacopyz_macro_def { + char *name; + char *value; +}; + +enum gacopyz_srv_state { + srv_init, + srv_connected, + srv_ready +}; + +struct gacopyz_srv { + struct gacopyz_iod iod; + enum gacopyz_srv_state state; + unsigned long flags; + gacopyz_macro_def_t def; + size_t ndefs; + size_t maxdefs; + gacopyz_uint32_t source_addr; + int version; + int acts; + int proto; + + char *buf; + size_t bufsize; +}; + +static struct gacopyz_macro_def * +find_def(gacopyz_srv_t srv, const char *name) +{ + size_t i; + for (i = 0; i < srv->ndefs; i++) + if (strcmp(srv->def[i].name, name) == 0) + return &srv->def[i]; + return NULL; +} + +static int +add_def(gacopyz_srv_t srv, const char *name, const char *value) +{ + struct gacopyz_macro_def *def; + + if (def = find_def(srv, name)) + free(def->value); + else { + if (srv->ndefs == srv->maxdefs) { + size_t maxdefs = srv->maxdefs + 64; + def = realloc(srv->def, + sizeof(srv->def[0]) * maxdefs); + if (!def) + return MI_FAILURE; + srv->maxdefs = maxdefs; + srv->def = def; + } + def = srv->def + srv->ndefs++; + def->name = strdup(name); + } + def->value = strdup(value); + if (!def->name || !def->value) + /* FIXME: Remove this entry from srv->dev */ + return MI_FAILURE; + return MI_SUCCESS; +} + +void +del_def(gacopyz_srv_t srv, const char *name) +{ + struct gacopyz_macro_def *def = find_def(srv, name); + size_t n; + + if (!def) + return; + + free(def->name); + free(def->value); + srv->ndefs--; + memmove(def, def + 1, + sizeof(srv->def[0]) *(srv->ndefs - (def - srv->def))); +} + + +int +gacopyz_srv_find_macro(gacopyz_srv_t srv, const char *name, const char **pval) +{ + struct gacopyz_macro_def *def; + + if (!srv) + return MI_FAILURE; + + def = find_def(srv, name); + if (def) { + *pval = def->value; + return MI_SUCCESS; + } + return MI_FAILURE; +} + +int +gacopyz_srv_define_macro(gacopyz_srv_t srv, + const char *name, const char *value) +{ + if (!srv) + return MI_FAILURE; + return add_def(srv, name, value); +} + +int +gacopyz_srv_del_macro(gacopyz_srv_t srv, const char *name) +{ + if (!srv) + return MI_FAILURE; + del_def(srv, name); + return MI_SUCCESS; +} + +int +gacopyz_srv_clear_macros(gacopyz_srv_t srv) +{ + size_t i; + + if (!srv) + return MI_FAILURE; + + for (i = 0; i < srv->ndefs; i++) { + free(srv->def[i].name); + free(srv->def[i].value); + } + srv->ndefs = 0; + return MI_SUCCESS; +} + +int +gacopyz_srv_iterate_macros(gacopyz_srv_t srv, + int (*func)(const char *name, const char *value, + void *data), + void *data) +{ + size_t i; + + if (!srv) + return MI_FAILURE; + + for (i = 0; i < srv->ndefs; i++) + if (func(srv->def[i].name, srv->def[i].value, data)) + break; + return MI_SUCCESS; +} + +int +gacopyz_srv_count_macros(gacopyz_srv_t srv, size_t *count) +{ + if (!srv) + return MI_FAILURE; + *count = srv->ndefs; + return MI_SUCCESS; +} + + + +int +gacopyz_srv_create(gacopyz_srv_t *p, unsigned logmask) +{ + gacopyz_srv_t srv = calloc(1, sizeof(*srv)); + if (!srv) + return MI_FAILURE; + srv->iod.sd = -1; + srv->iod.logmask = logmask; + srv->iod.timeout.tv_usec = 0; + srv->iod.timeout.tv_sec = 5; + srv->state = srv_init; + srv->source_addr = INADDR_ANY; + srv->version = SMFI_VERSION; + srv->proto = SMFI_V2_PROT; + srv->acts = SMFI_V2_ACTS; + *p = srv; + return MI_SUCCESS; +} + +int +gacopyz_srv_set_timeout(gacopyz_srv_t srv, time_t t) +{ + if (!srv) + return MI_FAILURE; + srv->iod.timeout.tv_usec = 0; + srv->iod.timeout.tv_sec = t; + return MI_SUCCESS; +} + +int +gacopyz_srv_set_source(gacopyz_srv_t srv, unsigned long addr) +{ + if (!srv) + return MI_FAILURE; + + srv->source_addr = htonl(addr); + return MI_SUCCESS; +} + +int +gacopyz_srv_set_version(gacopyz_srv_t srv, int version) +{ + if (!srv) + return MI_FAILURE; + + srv->version = version; + return MI_SUCCESS; +} + +int +gacopyz_srv_set_protocol(gacopyz_srv_t srv, int proto) +{ + if (!srv) + return MI_FAILURE; + + srv->proto = proto; + return MI_SUCCESS; +} + +int +gacopyz_srv_set_actions(gacopyz_srv_t srv, int acts) +{ + if (!srv) + return MI_FAILURE; + + srv->acts = acts; + return MI_SUCCESS; +} + + +void +gacopyz_srv_destroy(gacopyz_srv_t *p) +{ + gacopyz_srv_t srv = *p; + gacopyz_srv_clear_macros(srv); + free(srv->def); + free(srv->buf); + free(srv); + *p = 0; +} + +static int +srv_connect(gacopyz_srv_t srv, const char *cstr, + char *proto, char *port, char *path) +{ + union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_un sun; + } addr; + int socklen; + int fd = -1; + + if (!proto + || strcmp(proto, "unix") == 0 || strcmp(proto, "local") == 0) { + struct stat st; + + if (port) { + gacopyz_io_log(&srv->iod, SMI_LOG_ERR, + "invalid connection type: %s; " + "port is meaningless for UNIX sockets", + cstr); + return -1; + } + + if (strlen(path) > sizeof addr.sun.sun_path) { + errno = EINVAL; + gacopyz_io_log(&srv->iod, SMI_LOG_ERR, + "%s: UNIX socket name too long", + path); + return -1; + } + + addr.sa.sa_family = PF_UNIX; + socklen = sizeof(addr.sun); + strcpy(addr.sun.sun_path, path); + + if (stat(path, &st)) { + gacopyz_io_log(&srv->iod, SMI_LOG_ERR, + "%s: cannot stat socket: %s", + path, strerror(errno)); + return -1; + } else { + /* FIXME: Check permissions? */ + if (!S_ISSOCK(st.st_mode)) { + gacopyz_io_log(&srv->iod, SMI_LOG_ERR, + "%s: not a socket", + path); + return -1; + } + } + + } else if (strcmp(proto, "inet") == 0) { + short pnum; + long num; + char *p; + + addr.sa.sa_family = PF_INET; + socklen = sizeof(addr.sin); + + if (!port) { + gacopyz_io_log(&srv->iod, SMI_LOG_ERR, + "invalid connection type: %s; " + "missing port number", + cstr); + return -1; + } + + num = pnum = strtol(port, &p, 0); + if (*p == 0) { + if (num != pnum) { + gacopyz_io_log(&srv->iod, SMI_LOG_ERR, + "invalid connection type: %s; " + "bad port number", + cstr); + return -1; + } + pnum = htons(pnum); + } else { + struct servent *sp = getservbyname(path, "tcp"); + if (!sp) { + gacopyz_io_log(&srv->iod, SMI_LOG_ERR, + "invalid connection type: %s; " + "unknown port name", + cstr); + return -1; + } + pnum = sp->s_port; + } + + if (!path) + addr.sin.sin_addr.s_addr = INADDR_ANY; + else { + struct hostent *hp = gethostbyname(path); + if (!hp) { + gacopyz_io_log(&srv->iod, SMI_LOG_ERR, + "Unknown host name %s", + path); + return -1; + } + addr.sa.sa_family = hp->h_addrtype; + switch (hp->h_addrtype) { + case AF_INET: + memmove(&addr.sin.sin_addr, hp->h_addr, 4); + addr.sin.sin_port = pnum; + break; + + default: + gacopyz_io_log(&srv->iod, SMI_LOG_ERR, + "invalid connection type: %s; " + "unsupported address family", + cstr); + return -1; + } + } + + } else { + gacopyz_io_log(&srv->iod, SMI_LOG_ERR, + "unsupported protocol: %s", + proto); + return -1; + } + + fd = socket(addr.sa.sa_family, SOCK_STREAM, 0); + if (fd == -1) { + gacopyz_io_log(&srv->iod, SMI_LOG_ERR, + "Unable to create new socket: %s", + strerror(errno)); + return -1; + } + + if (addr.sa.sa_family == PF_INET) { + struct sockaddr_in s; + s.sin_family = AF_INET; + s.sin_addr.s_addr = srv->source_addr; + s.sin_port = 0; + + if (bind (fd, (struct sockaddr*) &s, sizeof(s)) < 0) { + gacopyz_io_log(&srv->iod, SMI_LOG_ERR, + "Cannot bind to local address: %s", + strerror(errno)); + close(fd); + return -1; + } + } + + if (connect(fd, &addr.sa, socklen)) { + gacopyz_io_log(&srv->iod, SMI_LOG_ERR, + "Cannot connect to %s: %s", + cstr, strerror(errno)); + close(fd); + return -1; + } + + return fd; +} + +int +gacopyz_srv_connect(gacopyz_sr |