Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge remote-tracking branch 'EmericBr/UpdateSHP'

Conflicts:
	stud.c
  • Loading branch information...
commit caa8fde8de0d390830055ef8976ddeae604049d3 2 parents 36d1e46 + 7191c81
Jamie Turner authored
Showing with 569 additions and 8 deletions.
  1. +68 −6 shctx.c
  2. +33 −0 shctx.h
  3. +468 −2 stud.c
74 shctx.c
View
@@ -18,13 +18,10 @@
#include "ebtree/ebmbtree.h"
#include "shctx.h"
-#ifndef SHSESS_MAX_DATA_LEN
-#define SHSESS_MAX_DATA_LEN 512
-#endif
-
struct shared_session {
struct ebmb_node key;
unsigned char key_data[SSL_MAX_SSL_SESSION_ID_LENGTH];
+ long c_date;
int data_len;
unsigned char data[SHSESS_MAX_DATA_LEN];
struct shared_session *p;
@@ -45,6 +42,9 @@ struct shared_context {
/* Static shared context */
static struct shared_context *shctx = NULL;
+/* Callbacks */
+static void (*shared_session_new_cbk)(unsigned char *session, unsigned int session_len, long cdate);
+
/* Lock functions */
#ifdef USE_SYSCALL_FUTEX
@@ -166,8 +166,9 @@ static inline void shared_context_unlock(void)
int shctx_new_cb(SSL *ssl, SSL_SESSION *sess) {
(void)ssl;
struct shared_session *shsess;
- unsigned char data[SHSESS_MAX_DATA_LEN],*p;
+ unsigned char *data,*p;
unsigned int data_len;
+ unsigned char encsess[SHSESS_MAX_ENCODED_LEN];
/* check if session reserved size in aligned buffer is large enougth for the ASN1 encode session */
data_len=i2d_SSL_SESSION(sess, NULL);
@@ -175,7 +176,7 @@ int shctx_new_cb(SSL *ssl, SSL_SESSION *sess) {
return 1;
/* process ASN1 session encoding before the lock: lower cost */
- p = data;
+ p = data = encsess+SSL_MAX_SSL_SESSION_ID_LENGTH;
i2d_SSL_SESSION(sess, &p);
shared_context_lock();
@@ -193,10 +194,23 @@ int shctx_new_cb(SSL *ssl, SSL_SESSION *sess) {
shsess->data_len = data_len;
memcpy(shsess->data, data, data_len);
+ /* store creation date */
+ shsess->c_date = SSL_SESSION_get_time(sess);
+
shsess_set_active(shsess);
shared_context_unlock();
+ if (shared_session_new_cbk) { /* if user level callback is set */
+
+ /* copy sessionid padded with 0 into the sessionid + data aligned buffer */
+ memcpy(encsess, sess->session_id, sess->session_id_length);
+ if (sess->session_id_length < SSL_MAX_SSL_SESSION_ID_LENGTH)
+ memset(encsess+sess->session_id_length, 0, SSL_MAX_SSL_SESSION_ID_LENGTH-sess->session_id_length);
+
+ shared_session_new_cbk(encsess, SSL_MAX_SSL_SESSION_ID_LENGTH+data_len, SSL_SESSION_get_time(sess));
+ }
+
return 0; /* do not increment session reference count */
}
@@ -207,6 +221,7 @@ SSL_SESSION *shctx_get_cb(SSL *ssl, unsigned char *key, int key_len, int *do_cop
unsigned char data[SHSESS_MAX_DATA_LEN], *p;
unsigned char tmpkey[SSL_MAX_SSL_SESSION_ID_LENGTH];
unsigned int data_len;
+ long cdate;
SSL_SESSION *sess;
/* allow the session to be freed automatically by openssl */
@@ -230,6 +245,9 @@ SSL_SESSION *shctx_get_cb(SSL *ssl, unsigned char *key, int key_len, int *do_cop
return NULL;
}
+ /* backup creation date to reset in session after ASN1 decode */
+ cdate = shsess->c_date;
+
/* copy ASN1 session data to decode outside the lock */
data_len = shsess->data_len;
memcpy(data, shsess->data, shsess->data_len);
@@ -242,6 +260,10 @@ SSL_SESSION *shctx_get_cb(SSL *ssl, unsigned char *key, int key_len, int *do_cop
p = data;
sess = d2i_SSL_SESSION(NULL, (const unsigned char **)&p, data_len);
+ /* reset creation date */
+ if (sess)
+ SSL_SESSION_set_time(sess, cdate);
+
return sess;
}
@@ -271,6 +293,46 @@ void shctx_remove_cb(SSL_CTX *ctx, SSL_SESSION *sess) {
shared_context_unlock();
}
+/* User level function called to add a session to the cache (remote updates) */
+void shctx_sess_add(const unsigned char *encsess, unsigned int len, long cdate) {
+ struct shared_session *shsess;
+
+ /* check buffer is at least padded key long + 1 byte
+ and data_len not too long */
+ if ( (len <= SSL_MAX_SSL_SESSION_ID_LENGTH)
+ || (len > SHSESS_MAX_DATA_LEN+SSL_MAX_SSL_SESSION_ID_LENGTH) )
+ return;
+
+
+ shared_context_lock();
+
+ shsess = shsess_get_next();
+
+ shsess_tree_delete(shsess);
+
+ shsess_set_key(shsess, encsess, SSL_MAX_SSL_SESSION_ID_LENGTH);
+
+ /* it returns the already existing node or current node if none, never returns null */
+ shsess = shsess_tree_insert(shsess);
+
+ /* store into cache and update earlier on session get events */
+ if (cdate)
+ shsess->c_date = (long)cdate;
+
+ /* copy ASN1 session data into cache */
+ shsess->data_len = len-SSL_MAX_SSL_SESSION_ID_LENGTH;
+ memcpy(shsess->data, encsess+SSL_MAX_SSL_SESSION_ID_LENGTH, shsess->data_len);
+
+ shsess_set_active(shsess);
+
+ shared_context_unlock();
+}
+
+/* Function used to set a callback on new session creation */
+void shsess_set_new_cbk(void (*func)(unsigned char *, unsigned int, long)) {
+ shared_session_new_cbk = func;
+}
+
/* Init shared memory context if not allocated and set SSL context callbacks
* size is the max number of stored session
* Returns: -1 on alloc failure, size if performs context alloc, and 0 if just perform
33 shctx.h
View
@@ -10,6 +10,39 @@
#ifndef SHCTX_H
#define SHCTX_H
#include <openssl/ssl.h>
+#include <stdint.h>
+
+#ifndef SHSESS_MAX_FOOTER_LEN
+#define SHSESS_MAX_FOOTER_LEN sizeof(uint32_t) \
+ + EVP_MAX_MD_SIZE
+#endif
+
+#ifndef SHSESS_MAX_DATA_LEN
+#define SHSESS_MAX_DATA_LEN 512
+#endif
+
+#define SHSESS_MAX_ENCODED_LEN SSL_MAX_SSL_SESSION_ID_LENGTH \
+ + SHSESS_MAX_DATA_LEN \
+ + SHSESS_MAX_FOOTER_LEN
+
+
+/* Callback called on a new session event:
+ * session contains the sessionid zeros padded to SSL_MAX_SSL_SESSION_ID_LENGTH
+ * followed by ASN1 session encoding.
+ * len is set to SSL_MAX_SSL_SESSION_ID_LENGTH + ASN1 session length
+ * len is always less than SSL_MAX_SSL_SESSION_ID_LENGTH + SHSESS_MAX_DATA_LEN.
+ * Remaining Bytes from len to SHSESS_MAX_ENCODED_LEN can be used to add a footer.
+ * cdate is the creation date timestamp.
+ */
+void shsess_set_new_cbk(void (*func)(unsigned char *session, unsigned int len, long cdate));
+
+/* Add a session into the cache,
+ * session contains the sessionid zeros padded to SSL_MAX_SSL_SESSION_ID_LENGTH
+ * followed by ASN1 session encoding.
+ * len is set to SSL_MAX_SSL_SESSION_ID_LENGTH + ASN1 data length.
+ * if len greater than SHSESS_MAX_ENCODED_LEN, session is not added.
+ * if cdate not 0, on get events session creation date will be reset to cdate */
+void shctx_sess_add(const unsigned char *session, unsigned int session_len, long cdate);
/* Init shared memory context if not allocated and set SSL context callbacks
* size is the max number of stored session
470 stud.c
View
@@ -34,6 +34,7 @@
#include <sys/wait.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
+#include <net/if.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
@@ -47,6 +48,7 @@
#include <limits.h>
#include <syslog.h>
+#include <ctype.h>
#include <sched.h>
#include <signal.h>
@@ -74,6 +76,12 @@
# define SOL_TCP IPPROTO_TCP
#endif
+#ifdef USE_SHARED_CACHE
+#ifndef MAX_SHCUPD_PEERS
+# define MAX_SHCUPD_PEERS 15
+#endif
+#endif
+
/* Globals */
static struct ev_loop *loop;
static struct addrinfo *backaddr;
@@ -83,6 +91,18 @@ static int listener_socket;
static int child_num;
static pid_t *child_pids;
static SSL_CTX *ssl_ctx;
+#ifdef USE_SHARED_CACHE
+static ev_io shcupd_listener;
+static int shcupd_socket;
+struct addrinfo *shcupd_peers[MAX_SHCUPD_PEERS+1];
+static unsigned char shared_secret[SHA_DIGEST_LENGTH];
+
+typedef struct shcupd_peer_opt {
+ const char *ip;
+ const char *port;
+} shcupd_peer_opt;
+
+#endif /*USE_SHARED_CACHE*/
/* Command line Options */
typedef enum {
@@ -108,6 +128,11 @@ typedef struct stud_options {
int BACKLOG;
#ifdef USE_SHARED_CACHE
int SHARED_CACHE;
+ const char *SHCUPD_IP;
+ const char *SHCUPD_PORT;
+ shcupd_peer_opt SHCUPD_PEERS[MAX_SHCUPD_PEERS+1];
+ const char *SHCUPD_MCASTIF;
+ const char *SHCUPD_MCASTTTL;
#endif
int QUIET;
int SYSLOG;
@@ -132,6 +157,11 @@ static stud_options OPTIONS = {
100, // BACKLOG
#ifdef USE_SHARED_CACHE
0, // SHARED_CACHE
+ NULL, // SHCUPD_IP
+ NULL, // SHCUPD_PORT
+ { { NULL, NULL } }, // SHCUPD_PEERS
+ NULL, // SHCUPD_MCASTIF
+ NULL, // SHCUPD_MCASTTTL
#endif
0, // QUIET
0, // SYSLOG
@@ -265,6 +295,280 @@ static void info_callback(const SSL *ssl, int where, int ret) {
}
}
+#ifdef USE_SHARED_CACHE
+
+/* Handle incoming message updates */
+static void handle_shcupd(struct ev_loop *loop, ev_io *w, int revents) {
+ (void) revents;
+ unsigned char msg[SHSESS_MAX_ENCODED_LEN], hash[EVP_MAX_MD_SIZE];
+ ssize_t r;
+ unsigned int hash_len;
+ uint32_t encdate;
+ long now = (time_t)ev_now(loop);
+
+ while ( ( r = recv(w->fd, msg, sizeof(msg), 0) ) > 0 ) {
+
+ /* msg len must be greater than 1 Byte of data + sig length */
+ if (r < (int)(1+sizeof(shared_secret)))
+ continue;
+
+ /* compute sig */
+ r -= sizeof(shared_secret);
+ HMAC(EVP_sha1(), shared_secret, sizeof(shared_secret), msg, r, hash, &hash_len);
+
+ if (hash_len != sizeof(shared_secret)) /* should never append */
+ continue;
+
+ /* check sign */
+ if(memcmp(msg+r, hash, hash_len))
+ continue;
+
+ /* msg len must be greater than 1 Byte of data + encdate length */
+ if (r < (int)(1+sizeof(uint32_t)))
+ continue;
+
+ /* drop too unsync updates */
+ r -= sizeof(uint32_t);
+ encdate = *((uint32_t *)&msg[r]);
+ if (!(abs((int)(int32_t)now-ntohl(encdate)) < SSL_CTX_get_timeout(ssl_ctx)))
+ continue;
+
+ shctx_sess_add(msg, r, now);
+ }
+}
+
+/* Send remote updates messages callback */
+void shcupd_session_new(unsigned char *msg, unsigned int len, long cdate) {
+ unsigned int hash_len;
+ struct addrinfo **pai = shcupd_peers;
+ uint32_t ncdate;
+
+ /* add session creation encoded date to footer */
+ ncdate = htonl((uint32_t)cdate);
+ memcpy(msg+len, &ncdate, sizeof(ncdate));
+ len += sizeof(ncdate);
+
+ /* add msg sign */
+ HMAC(EVP_sha1(), shared_secret, sizeof(shared_secret),
+ msg, len, msg+len, &hash_len);
+ len += hash_len;
+
+ /* send msg to peers */
+ while (*pai) {
+ sendto(shcupd_socket, msg, len, 0, (*pai)->ai_addr, (*pai)->ai_addrlen);
+ pai++;
+ }
+}
+
+/* Compute a sha1 secret from an ASN1 rsa private key */
+static int compute_secret(RSA *rsa, unsigned char *secret) {
+ unsigned char *buf,*p;
+ unsigned int length;
+
+ length = i2d_RSAPrivateKey(rsa, NULL);
+ if (length <= 0)
+ return -1;
+
+ p = buf = (unsigned char *)malloc(length*sizeof(unsigned char));
+ if (!buf)
+ return -1;
+
+ i2d_RSAPrivateKey(rsa,&p);
+
+ SHA1(buf, length, secret);
+
+ free(buf);
+
+ return 0;
+}
+
+/* Create udp socket to receive and send updates */
+static int create_shcupd_socket() {
+ struct addrinfo *ai, hints;
+ struct addrinfo **pai = shcupd_peers;
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
+ const int gai_err = getaddrinfo(OPTIONS.SHCUPD_IP, OPTIONS.SHCUPD_PORT,
+ &hints, &ai);
+ if (gai_err != 0) {
+ ERR("{getaddrinfo}: [%s]\n", gai_strerror(gai_err));
+ exit(1);
+ }
+
+ /* check if peers inet family addresses match */
+ while (*pai) {
+ if ((*pai)->ai_family != ai->ai_family) {
+ ERR("Share host and peers inet family differs\n");
+ exit(1);
+ }
+ pai++;
+ }
+
+ int s = socket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP);
+
+ if (s == -1)
+ fail("{socket: shared cache updates}");
+
+ int t = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &t, sizeof(int));
+#ifdef SO_REUSEPORT
+ setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &t, sizeof(int));
+#endif
+
+ setnonblocking(s);
+
+ if (ai->ai_addr->sa_family == AF_INET) {
+ struct ip_mreqn mreqn;
+
+ memset(&mreqn, 0, sizeof(mreqn));
+ mreqn.imr_multiaddr.s_addr = ((struct sockaddr_in *)ai->ai_addr)->sin_addr.s_addr;
+
+ if (OPTIONS.SHCUPD_MCASTIF) {
+ if (isalpha(*OPTIONS.SHCUPD_MCASTIF)) { /* appears to be an iface name */
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ if (strlen(OPTIONS.SHCUPD_MCASTIF) > IFNAMSIZ) {
+ ERR("Error iface name is too long [%s]\n",OPTIONS.SHCUPD_MCASTIF);
+ exit(1);
+ }
+
+ memcpy(ifr.ifr_name, OPTIONS.SHCUPD_MCASTIF, strlen(OPTIONS.SHCUPD_MCASTIF));
+ if (ioctl(s, SIOCGIFINDEX, &ifr)) {
+ fail("{ioctl: SIOCGIFINDEX}");
+ }
+
+ mreqn.imr_ifindex = ifr.ifr_ifindex;
+ }
+ else if (strchr(OPTIONS.SHCUPD_MCASTIF,'.')) { /* appears to be an ipv4 address */
+ mreqn.imr_address.s_addr = inet_addr(OPTIONS.SHCUPD_MCASTIF);
+ }
+ else { /* appears to be an iface index */
+ mreqn.imr_ifindex = atoi(OPTIONS.SHCUPD_MCASTIF);
+ }
+ }
+
+ if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn, sizeof(mreqn)) < 0) {
+ if (errno != EINVAL) { /* EINVAL if it is not a multicast address,
+ not an error we consider unicast */
+ fail("{setsockopt: IP_ADD_MEMBERSIP}");
+ }
+ }
+ else { /* this is a multicast address */
+ unsigned char loop = 0;
+
+ if(setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0) {
+ fail("{setsockopt: IP_MULTICAST_LOOP}");
+ }
+ }
+
+ /* optional set sockopts for sending to multicast msg */
+ if (OPTIONS.SHCUPD_MCASTIF &&
+ setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &mreqn, sizeof(mreqn)) < 0) {
+ fail("{setsockopt: IP_MULTICAST_IF}");
+ }
+
+ if (OPTIONS.SHCUPD_MCASTTTL) {
+ unsigned char ttl;
+
+ ttl = (unsigned char)atoi(OPTIONS.SHCUPD_MCASTTTL);
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
+ fail("{setsockopt: IP_MULTICAST_TTL}");
+ }
+ }
+
+ }
+#ifdef IPV6_ADD_MEMBERSHIP
+ else if (ai->ai_addr->sa_family == AF_INET6) {
+ struct ipv6_mreq mreq;
+
+ memset(&mreq, 0, sizeof(mreq));
+ memcpy(&mreq.ipv6mr_multiaddr, &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr,
+ sizeof(mreq.ipv6mr_multiaddr));
+
+ if (OPTIONS.SHCUPD_MCASTIF) {
+ if (isalpha(*OPTIONS.SHCUPD_MCASTIF)) { /* appears to be an iface name */
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ if (strlen(OPTIONS.SHCUPD_MCASTIF) > IFNAMSIZ) {
+ ERR("Error iface name is too long [%s]\n",OPTIONS.SHCUPD_MCASTIF);
+ exit(1);
+ }
+
+ memcpy(ifr.ifr_name, OPTIONS.SHCUPD_MCASTIF, strlen(OPTIONS.SHCUPD_MCASTIF));
+ if (ioctl(s, SIOCGIFINDEX, &ifr)) {
+ fail("{ioctl: SIOCGIFINDEX}");
+ }
+
+ mreq.ipv6mr_interface = ifr.ifr_ifindex;
+ }
+ else { /* option appears to be an iface index */
+ mreq.ipv6mr_interface = atoi(OPTIONS.SHCUPD_MCASTIF);
+ }
+ }
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
+ if (errno != EINVAL) { /* EINVAL if it is not a multicast address,
+ not an error we consider unicast */
+ fail("{setsockopt: IPV6_ADD_MEMBERSIP}");
+ }
+ }
+ else { /* this is a multicast address */
+ unsigned int loop = 0;
+
+ if(setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)) < 0) {
+ fail("{setsockopt: IPV6_MULTICAST_LOOP}");
+ }
+ }
+
+ /* optional set sockopts for sending to multicast msg */
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF,
+ &mreq.ipv6mr_interface, sizeof(mreq.ipv6mr_interface)) < 0) {
+ fail("{setsockopt: IPV6_MULTICAST_IF}");
+ }
+
+ if (OPTIONS.SHCUPD_MCASTTTL) {
+ int hops;
+
+ hops = atoi(OPTIONS.SHCUPD_MCASTTTL);
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops)) < 0) {
+ fail("{setsockopt: IPV6_MULTICAST_HOPS}");
+ }
+ }
+ }
+#endif /* IPV6_ADD_MEMBERSHIP */
+
+ if (bind(s, ai->ai_addr, ai->ai_addrlen)) {
+ fail("{bind-socket}");
+ }
+
+ freeaddrinfo(ai);
+
+ return s;
+}
+
+#endif /*USE_SHARED_CACHE */
+
+RSA *load_rsa_privatekey(SSL_CTX *ctx, const char *file) {
+ BIO *bio;
+ RSA *rsa;
+
+ bio = BIO_new_file(file, "r");
+ if (!bio) {
+ ERR_print_errors_fp(stderr);
+ return NULL;
+ }
+
+ rsa = PEM_read_bio_RSAPrivateKey(bio, NULL,
+ ctx->default_passwd_callback, ctx->default_passwd_callback_userdata);
+ BIO_free(bio);
+
+ return rsa;
+}
+
/* Init library and load specified certificate.
* Establishes a SSL_ctx, to act as a template for
* each connection */
@@ -272,6 +576,8 @@ static SSL_CTX * init_openssl() {
SSL_library_init();
SSL_load_error_strings();
SSL_CTX *ctx = NULL;
+ RSA *rsa;
+
long ssloptions = SSL_OP_NO_SSLv2 | SSL_OP_ALL |
SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
@@ -293,7 +599,14 @@ static SSL_CTX * init_openssl() {
ERR_print_errors_fp(stderr);
exit(1);
}
- if (SSL_CTX_use_RSAPrivateKey_file(ctx, OPTIONS.CERT_FILE, SSL_FILETYPE_PEM) <= 0) {
+
+ rsa = load_rsa_privatekey(ctx, OPTIONS.CERT_FILE);
+ if(!rsa) {
+ ERR("Error loading rsa private key\n");
+ exit(1);
+ }
+
+ if (SSL_CTX_use_RSAPrivateKey(ctx,rsa) <= 0) {
ERR_print_errors_fp(stderr);
exit(1);
}
@@ -330,9 +643,23 @@ static SSL_CTX * init_openssl() {
ERR("Unable to alloc memory for shared cache.\n");
exit(1);
}
+ if (OPTIONS.SHCUPD_PORT) {
+ if (compute_secret(rsa, shared_secret) < 0) {
+ ERR("Unable to compute shared secret.\n");
+ exit(1);
+ }
+
+ /* Force tls tickets cause keys differs */
+ SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET);
+
+ if (*shcupd_peers) {
+ shsess_set_new_cbk(shcupd_session_new);
+ }
+ }
}
#endif
+ RSA_free(rsa);
return ctx;
}
@@ -930,6 +1257,11 @@ static void usage_fail(const char *prog, const char *msg) {
"Socket:\n"
" -b HOST,PORT backend [connect] (default is \"127.0.0.1,8000\")\n"
" -f HOST,PORT frontend [bind] (default is \"*,8443\")\n"
+#ifdef USE_SHARED_CACHE
+" -U HOST,PORT accept cache updates on udp (default disabled, needs shared cache enabled)\n"
+" -P HOST[,PORT] send cache updates to peer (multiple option, needs updates enabled)\n"
+" -M IFACE[,TTL] force iface and ttl to receive and send multicast updates\n"
+#endif
"\n"
"Performance:\n"
" -n CORES number of worker processes (default is 1)\n"
@@ -987,6 +1319,85 @@ static void parse_host_and_port(const char *prog, const char *name, char *inp, i
*port = sp + 1;
}
+#ifdef USE_SHARED_CACHE
+/* Parse mcast and ttl options */
+static void parse_mcast_iface_and_ttl(const char *prog, const char *name, char *inp, const char **iface, const char **ttl) {
+ char buf[150];
+ char *sp;
+
+ if (strlen(inp) >= sizeof buf) {
+ snprintf(buf, sizeof buf, "invalid option for %s IFACE[,TTL]\n", name);
+ usage_fail(prog, buf);
+ }
+
+ sp = strchr(inp, ',');
+ if (!sp) {
+ if (!strcmp(inp, "*"))
+ *iface = NULL;
+ else
+ *iface = inp;
+ *ttl = NULL;
+ return;
+ }
+ else if (!strncmp(inp, "*", sp - inp)) {
+ *iface = NULL;
+ }
+ else {
+ *sp = 0;
+ *iface = inp;
+ }
+ *ttl = sp + 1;
+}
+
+static void parse_peer(const char *prog, const char *name, char *inp, const char **ip, const char **port) {
+ char buf[150];
+ char *sp;
+
+ if (strlen(inp) >= sizeof buf) {
+ snprintf(buf, sizeof buf, "invalid option for %s HOST[,PORT]\n", name);
+ usage_fail(prog, buf);
+ }
+
+ sp = strchr(inp, ',');
+ if (!sp) {
+ if (!strcmp(inp, "*")) {
+ snprintf(buf, sizeof buf, "wildcard host specification invalid for %s\n", name);
+ usage_fail(prog, buf);
+ }
+ else
+ *ip = inp;
+ *port = NULL;
+ return;
+ }
+ else if (!strncmp(inp, "*", sp - inp)) {
+ snprintf(buf, sizeof buf, "wildcard host specification invalid for %s\n", name);
+ usage_fail(prog, buf);
+ }
+ else {
+ *sp = 0;
+ *ip = inp;
+ }
+ *port = sp + 1;
+}
+
+/* Add shcupd peer to options */
+static void parse_peers(const char *prog, const char *name, char *inp) {
+ int i;
+
+ for (i = 0 ; i < MAX_SHCUPD_PEERS; i++) {
+ if (!OPTIONS.SHCUPD_PEERS[i].ip) {
+ parse_peer(prog, name, inp, &(OPTIONS.SHCUPD_PEERS[i].ip), &(OPTIONS.SHCUPD_PEERS[i].port));
+ memset(&(OPTIONS.SHCUPD_PEERS[i+1]), 0, sizeof(shcupd_peer_opt));
+ break;
+ }
+ }
+ if (i == MAX_SHCUPD_PEERS ) {
+ ERR("Maximum %s peers reach (%d)\n", name, MAX_SHCUPD_PEERS);
+ exit(1);
+ }
+}
+
+#endif /* USE_SHARED_CACHE */
/* Handle command line arguments modifying behavior */
static void parse_cli(int argc, char **argv) {
@@ -1007,7 +1418,7 @@ static void parse_cli(int argc, char **argv) {
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "hf:b:n:c:e:u:r:B:C:k:qs",
+ c = getopt_long(argc, argv, "hf:b:n:c:e:u:r:B:C:k:qsU:P:M:",
long_options, &option_index);
if (c == -1)
@@ -1082,6 +1493,18 @@ static void parse_cli(int argc, char **argv) {
exit(1);
}
break;
+
+ case 'U':
+ parse_host_and_port(prog, "-U", optarg, 1, &(OPTIONS.SHCUPD_IP), &(OPTIONS.SHCUPD_PORT));
+ break;
+
+ case 'P':
+ parse_peers(prog, "-P", optarg);
+ break;
+
+ case 'M':
+ parse_mcast_iface_and_ttl(prog, "-M", optarg, &(OPTIONS.SHCUPD_MCASTIF), &(OPTIONS.SHCUPD_MCASTTTL));
+ break;
#endif
case 'q':
@@ -1150,6 +1573,28 @@ void init_globals() {
exit(1);
}
+#ifdef USE_SHARED_CACHE
+ if (OPTIONS.SHARED_CACHE) {
+ /* cache update peers addresses */
+ shcupd_peer_opt *spo = OPTIONS.SHCUPD_PEERS;
+ struct addrinfo **pai = shcupd_peers;
+
+ while (spo->ip) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = 0;
+ const int gai_err = getaddrinfo(spo->ip,
+ spo->port ? spo->port : OPTIONS.SHCUPD_PORT, &hints, pai);
+ if (gai_err != 0) {
+ ERR("{getaddrinfo}: [%s]", gai_strerror(gai_err));
+ exit(1);
+ }
+ spo++;
+ pai++;
+ }
+ }
+#endif
/* child_pids */
if ((child_pids = calloc(OPTIONS.NCORES, sizeof(pid_t))) == NULL)
fail("calloc");
@@ -1255,6 +1700,14 @@ int main(int argc, char **argv) {
listener_socket = create_main_socket();
+#ifdef USE_SHARED_CACHE
+ if (OPTIONS.SHCUPD_PORT) {
+ /* create socket to send(children) and
+ receive(parent) cache updates */
+ shcupd_socket = create_shcupd_socket();
+ }
+#endif /* USE_SHARED_CACHE */
+
/* load certificate, pass to handle_connections */
ssl_ctx = init_openssl();
@@ -1268,6 +1721,19 @@ int main(int argc, char **argv) {
start_children(0, OPTIONS.NCORES);
+#ifdef USE_SHARED_CACHE
+ if (OPTIONS.SHCUPD_PORT) {
+ /* start event loop to receive cache updates */
+
+ loop = ev_default_loop(EVFLAG_AUTO);
+
+ ev_io_init(&shcupd_listener, handle_shcupd, shcupd_socket, EV_READ);
+ ev_io_start(loop, &shcupd_listener);
+
+ ev_loop(loop, 0);
+ }
+#endif /* USE_SHARED_CACHE */
+
for (;;) {
/* Sleep and let the children work.
* Parent will be woken up if a signal arrives */
Please sign in to comment.
Something went wrong with that request. Please try again.