aboutsummaryrefslogtreecommitdiff
path: root/gacopyz
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2007-03-01 23:08:32 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2007-03-01 23:08:32 +0000
commit7128853094a3df29948dc949ff04b43cccd400f7 (patch)
tree69db0978e6b835b7a736490a06a4307b1e32aa0c /gacopyz
parentc6eecf2842218ed97a7084fbecae06388ec358f3 (diff)
downloadmailfromd-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.am4
-rw-r--r--gacopyz/gacopyz.c61
-rw-r--r--gacopyz/gacopyz.h69
-rw-r--r--gacopyz/gacopyz_priv.h25
-rw-r--r--gacopyz/io.c218
-rw-r--r--gacopyz/log.c60
-rw-r--r--gacopyz/server.c575
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