int main(int argc, char **argv) { int err, publish; hip_hit hit; struct sockaddr_storage addr; struct sockaddr_in *addr4 = (struct sockaddr_in*)&addr; /* * Load hip.conf configuration file * user may have provided path using command line, or search defaults */ memset(HCNF, 0, sizeof(HCNF)); if ((locate_config_file(HCNF.conf_filename, sizeof(HCNF.conf_filename), HIP_CONF_FILENAME) < 0) || (read_conf_file(HCNF.conf_filename) < 0)) { log_(ERR, "Problem with configuration file, using defaults.\n"); } else { log_(NORM, "Using configuration file:\t%s\n", HCNF.conf_filename); } memset(&addr, 0, sizeof(addr)); addr4->sin_family = AF_INET; addr4->sin_addr.s_addr = inet_addr("192.168.1.2"); hex_to_bin("7BE901B3AF2679C8C580619535641713", hit, HIT_SIZE); printf("Doing XML RPC put 1...\n"); err = hip_dht_publish(&hit, (struct sockaddr*)&addr); printf("return value = %d\n", err); addr4->sin_addr.s_addr = inet_addr("192.168.2.7"); printf("Doing XML RPC put 2...\n"); err = hip_dht_publish(&hit, (struct sockaddr*)&addr); printf("return value = %d\n", err); memset(&addr, 0, sizeof(addr)); addr4->sin_family = AF_INET; printf("addr is at: %p\n", &addr); printf("Doing XML RPC get...\n"); err = hip_dht_lookup_address(&hit, (struct sockaddr*)&addr); printf("return value = %d\n", err); printf("Address = %s\n", logaddr((struct sockaddr*)&addr)); return(0); }
int std_subscribe(const char *dir, const char *subdir, const char *userhost, int flagadd, const char *comment, const char *event, int forcehash) /* add (flagadd=1) or remove (flagadd=0) userhost from the subscr. database */ /* dbname. Comment is e.g. the subscriber from line or name. It is added to */ /* the log. Event is the action type, e.g. "probe", "manual", etc. The */ /* direction (sub/unsub) is inferred from flagadd. Returns 1 on success, 0 */ /* on failure. If flagmysql is set and the file "sql" is found in the */ /* directory dbname, it is parsed and a mysql db is assumed. if forcehash is */ /* >=0 it is used in place of the calculated hash. This makes it possible to */ /* add addresses with a hash that does not exist. forcehash has to be 0..99. */ /* for unsubscribes, the address is only removed if forcehash matches the */ /* actual hash. This way, ezmlm-manage can be prevented from touching certain*/ /* addresses that can only be removed by ezmlm-unsub. Usually, this would be */ /* used for sublist addresses (to avoid removal) and sublist aliases (to */ /* prevent users from subscribing them (although the cookie mechanism would */ /* prevent the resulting duplicate message from being distributed. */ { int fdlock; unsigned int j; unsigned char ch,lcch; int match; int flagwasthere; if (userhost[str_chr(userhost,'\n')]) strerr_die2x(100,FATAL,ERR_ADDR_NL); if (!stralloc_copys(&addr,"T")) die_nomem(); if (!stralloc_cats(&addr,userhost)) die_nomem(); if (addr.len > 401) strerr_die2x(100,FATAL,ERR_ADDR_LONG); j = byte_rchr(addr.s,addr.len,'@'); if (j == addr.len) strerr_die2x(100,FATAL,ERR_ADDR_AT); case_lowerb(addr.s + j + 1,addr.len - j - 1); if (!stralloc_copy(&lcaddr,&addr)) die_nomem(); case_lowerb(lcaddr.s + 1,j - 1); /* make all-lc version of address */ if (forcehash >= 0 && forcehash <= 52) { ch = lcch = 64 + (unsigned char) forcehash; } else { ch = 64 + subhashsa(&addr); lcch = 64 + subhashsa(&lcaddr); } if (!stralloc_0(&addr)) die_nomem(); if (!stralloc_0(&lcaddr)) die_nomem(); std_makepath(&fn,dir,subdir,"/subscribers/",lcch); std_makepath(&fnlock,dir,subdir,"/lock",0); if (!stralloc_copyb(&fnnew,fn.s,fn.len-1)) die_nomem(); /* code later depends on fnnew = fn + 'n' */ if (!stralloc_cats(&fnnew,"n")) die_nomem(); if (!stralloc_0(&fnnew)) die_nomem(); fdlock = lockfile(fnlock.s); /* do lower case hashed version first */ fdnew = open_trunc(fnnew.s); if (fdnew == -1) die_write(); substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf)); flagwasthere = 0; fd = open_read(fn.s); if (fd == -1) { if (errno != error_noent) { close(fdnew); die_read(); } } else { substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf)); for (;;) { if (getln(&ss,&line,&match,'\0') == -1) { close(fd); close(fdnew); die_read(); } if (!match) break; if (line.len == addr.len) if (!case_diffb(line.s,line.len,addr.s)) { flagwasthere = 1; if (!flagadd) continue; } if (substdio_bput(&ssnew,line.s,line.len) == -1) { close(fd); close(fdnew); die_write(); } } close(fd); } if (flagadd && !flagwasthere) if (substdio_bput(&ssnew,addr.s,addr.len) == -1) { close(fdnew); die_write(); } if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(); } if (fsync(fdnew) == -1) { close(fdnew); die_write(); } close(fdnew); if (rename(fnnew.s,fn.s) == -1) strerr_die6sys(111,FATAL,ERR_MOVE,fnnew.s," to ",fn.s,": "); if ((ch == lcch) || flagwasthere) { close(fdlock); if (flagadd ^ flagwasthere) { if (!stralloc_0(&addr)) die_nomem(); logaddr(dir,subdir,event,addr.s+1,comment); return 1; } return 0; } /* If unsub and not found and hashed differ, OR */ /* sub and not found (so added with new hash) */ /* do the 'case-dependent' hash */ fn.s[fn.len - 2] = ch; fnnew.s[fnnew.len - 3] = ch; fdnew = open_trunc(fnnew.s); if (fdnew == -1) die_write(); substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf)); fd = open_read(fn.s); if (fd == -1) { if (errno != error_noent) { close(fdnew); die_read(); } } else { substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf)); for (;;) { if (getln(&ss,&line,&match,'\0') == -1) { close(fd); close(fdnew); die_read(); } if (!match) break; if (line.len == addr.len) if (!case_diffb(line.s,line.len,addr.s)) { flagwasthere = 1; continue; /* always want to remove from case-sensitive hash */ } if (substdio_bput(&ssnew,line.s,line.len) == -1) { close(fd); close(fdnew); die_write(); } } close(fd); } if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(); } if (fsync(fdnew) == -1) { close(fdnew); die_write(); } close(fdnew); if (rename(fnnew.s,fn.s) == -1) strerr_die6sys(111,FATAL,ERR_MOVE,fnnew.s," to ",fn.s,": "); close(fdlock); if (flagadd ^ flagwasthere) { if (!stralloc_0(&addr)) die_nomem(); logaddr(dir,subdir,event,addr.s+1,comment); return 1; } return 0; }
/* * \fn hip_xmlrpc_getput() * * \param mode determines get or put, app, retry on/off * If retry is off only one attempt should be made, * on means the connect() should keep retrying * \param app string to use in the XML RPC application field * \param server server address and port to connect to * \param key DHT key used for get or put * \param key_len length of DHT key in bytes * \param value DHT value used for put, ptr for storing value for get * \param value_len ptr to length of value buffer, length of get is returned * \param secret secret value used to make put removable * \param secret_len length of secret value * \param ttl time to live in seconds * * \brief Perform the XML RPC GET, PUT, and RM operations. */ int hip_xmlrpc_getput(int mode, char *app, struct sockaddr *server, char *key, int key_len, char *value, int *value_len, char *secret, int secret_len, int ttl) { xmlDocPtr doc = NULL; xmlNodePtr root_node = NULL, node; int len = 0, s, retval = 0; char buff[2048], oper[14]; unsigned char key64[2 * DHT_KEY_SIZE], val64[2 * DHT_VAL_SIZE]; unsigned char tmp[2 * DHT_VAL_SIZE], *xmlbuff = NULL; fd_set read_fdset; struct timeval timeout, now; char *p; unsigned int retry_attempts = 0; struct sockaddr_in src_addr; struct dht_val *dv, rm; SHA_CTX c; __u8 secret_hash[SHA_DIGEST_LENGTH], value_hash[SHA_DIGEST_LENGTH]; int rm_ttl = 0, value_hash_len; int retry = ((mode & 0x00F0) == XMLRPC_MODE_RETRY_ON); if ((key_len > (2 * DHT_KEY_SIZE)) || (*value_len > (2 * DHT_VAL_SIZE))) { return(-1); } /* * support for removable puts */ memset(&rm, 0, sizeof(struct dht_val)); if ((mode & 0x000F) == XMLRPC_MODE_PUT) { /* * produce hashes of the secret and the value, for later removal */ SHA1_Init(&c); SHA1_Update(&c, value, *value_len); SHA1_Final(value_hash, &c); SHA1_Init(&c); SHA1_Update(&c, secret, secret_len); SHA1_Final(secret_hash, &c); /* * check if we already published a record with this key; record * this new secret value and value_hash */ pthread_mutex_lock(&dht_vals_lock); gettimeofday(&now, NULL); dv = lookup_dht_val(key); if (dv) { /* save old secret so we can remove it later below */ memcpy(&rm, &dv, sizeof(struct dht_val)); /* any time left for removing the old record? */ rm_ttl = TDIFF(rm.expire_time, now); } else { dv = insert_dht_val(key); } strncpy(dv->app, app, sizeof(dv->app)); dv->value_hash_len = SHA_DIGEST_LENGTH; memcpy(dv->value_hash, value_hash, SHA_DIGEST_LENGTH); dv->secret_len = secret_len; memcpy(dv->secret, secret, secret_len); dv->expire_time.tv_usec = now.tv_usec; dv->expire_time.tv_sec = now.tv_sec + ttl; pthread_mutex_unlock(&dht_vals_lock); } switch (mode & 0x000F) { case XMLRPC_MODE_PUT: sprintf(oper, "put_removable"); break; case XMLRPC_MODE_GET: sprintf(oper, "get"); break; case XMLRPC_MODE_RM: sprintf(oper, "rm"); break; default: log_(WARN, "Invalid XMLRPC mode given to DHT.\n"); return(-1); } /* * create a new XML document */ doc = xmlNewDoc(BAD_CAST "1.0"); root_node = xmlNewNode(NULL, BAD_CAST "methodCall"); xmlDocSetRootElement(doc, root_node); node = xmlNewChild(root_node, NULL, BAD_CAST "methodName", BAD_CAST oper); node = xmlNewChild(root_node, NULL, BAD_CAST "params", NULL); memset(tmp, 0, sizeof(tmp)); memcpy(tmp, key, key_len); EVP_EncodeBlock(key64, tmp, key_len); xml_new_param(node, "base64", (char *)key64); /* key */ /* log_(NORM, "Doing %s using key(%d)=", * ((mode & 0x000F)==XMLRPC_MODE_PUT) ? "PUT":"GET", key_len); * print_hex(key, key_len); * log_(NORM, " [%s]\n", key64); // */ switch (mode & 0x000F) { case XMLRPC_MODE_PUT: memset(tmp, 0, sizeof(tmp)); memcpy(tmp, value, *value_len); EVP_EncodeBlock(val64, tmp, *value_len); xml_new_param(node, "base64", (char *)val64); /* value */ xml_new_param(node, "string", "SHA"); /* hash type */ memset(tmp, 0, sizeof(tmp)); memcpy(tmp, secret_hash, SHA_DIGEST_LENGTH); EVP_EncodeBlock(val64, tmp, SHA_DIGEST_LENGTH); xml_new_param(node, "base64", (char *)val64); /* secret_hash */ sprintf((char *)tmp, "%d", ttl); xml_new_param(node, "int", (char *)tmp); /* lifetime */ break; case XMLRPC_MODE_GET: xml_new_param(node, "int", "10"); /* maxvals */ xml_new_param(node, "base64", ""); /* placemark */ memset(value, 0, *value_len); break; case XMLRPC_MODE_RM: memset(tmp, 0, sizeof(tmp)); memcpy(tmp, value_hash, SHA_DIGEST_LENGTH); EVP_EncodeBlock(val64, tmp, SHA_DIGEST_LENGTH); xml_new_param(node, "base64", (char *)val64); /* value_hash */ xml_new_param(node, "string", "SHA"); /* hash type */ memset(tmp, 0, sizeof(tmp)); memcpy(tmp, secret, secret_len); EVP_EncodeBlock(val64, tmp, secret_len); xml_new_param(node, "base64", (char *)val64); /* secret */ sprintf((char *)tmp, "%d", ttl); xml_new_param(node, "int", (char *)tmp); /* lifetime */ } xml_new_param(node, "string", app); /* app */ xmlDocDumpFormatMemory(doc, &xmlbuff, &len, 0); /* * Build an HTTP POST and transmit to server */ memset(buff, 0, sizeof(buff)); build_http_post_header(buff, len, server); /* len is XML length above */ memcpy(&buff[strlen(buff)], xmlbuff, len); xmlFree(xmlbuff); len = strlen(buff) + 1; connect_retry: /* Connect and send the XML RPC */ if ((s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { log_(WARN, "DHT connect - socket error: %s\n", strerror(errno)); retval = -1; goto putget_exit; } /* Use the preferred address as source */ memset(&src_addr, 0, sizeof(src_addr)); src_addr.sin_family = AF_INET; src_addr.sin_addr.s_addr = get_preferred_addr(); if (!src_addr.sin_addr.s_addr) { log_(NORM, "No preferred address, deferring DHT!\n"); return(-1); } log_(NORM, "Using source address of %s for DHT %s.\n", logaddr(SA(&src_addr)), oper); fflush(stdout); if (bind(s, SA(&src_addr), SALEN(&src_addr)) < 0) { log_(WARN, "DHT connect - bind error: %s\n", strerror(errno)); } if (g_state != 0) { return(-1); } if (retry && (retry_attempts > 0)) { /* quit after a certain number of retries */ if (retry_attempts >= HCNF.max_retries) { retval = -2; goto putget_exit; } /* wait packet_timeout seconds before retrying */ hip_sleep(HCNF.packet_timeout); } retry_attempts++; if (connect(s, server, SALEN(server)) < 0) { log_(WARN, "DHT server connect error: %s\n", strerror(errno)); closesocket(s); #ifdef __WIN32__ errno = WSAGetLastError(); if (retry && ((errno == WSAETIMEDOUT) || (errno == WSAENETUNREACH))) { goto connect_retry; } #else if (retry && ((errno == ETIMEDOUT) || (errno == EHOSTUNREACH))) { goto connect_retry; } #endif retval = -3; goto putget_exit; } if (send(s, buff, len, 0) != len) { log_(WARN, "DHT sent incorrect number of bytes\n"); retval = -4; goto putget_exit; } xmlFreeDoc(doc); doc = NULL; /* * Receive XML RPC response from server */ FD_ZERO(&read_fdset); FD_SET((unsigned int)s, &read_fdset); /* use longer timeout when retry==TRUE, because we have own thread */ if (retry) { timeout.tv_sec = 3; timeout.tv_usec = 0; } else { timeout.tv_sec = 0; timeout.tv_usec = 300000; /* 300ms */ } if (select(s + 1, &read_fdset, NULL, NULL, &timeout) < 0) { log_(WARN, "DHT select error: %s\n", strerror(errno)); retval = -5; goto putget_exit; } else if (FD_ISSET(s, &read_fdset)) { if ((len = recv(s, buff, sizeof(buff) - 1, 0)) <= 0) { log_(WARN, "DHT error receiving from server: %s\n", strerror(errno)); retval = -6; goto putget_exit; } if (strncmp(buff, "HTTP", 4) != 0) { return(-7); } if ((p = strstr(buff, "Content-Length: ")) == NULL) { return(-8); } else /* advance ptr to Content-Length */ { p += 16; } sscanf(p, "%d", &len); p = strchr(p, '\n') + 3; /* advance to end of line */ retval = hip_xmlrpc_parse_response(mode, p, len, value, value_len); log_(NORM, "DHT server responded with return code %d (%s).\n", retval, hip_xmlrpc_resp_to_str(retval)); } else { /* select timeout */ if (retry) /* XXX testme: retry select instead? */ { goto connect_retry; } retval = -9; } putget_exit: #ifdef __WIN32__ closesocket(s); #else close(s); #endif if (doc != NULL) { xmlFreeDoc(doc); } if (rm_ttl > 0) { value_hash_len = sizeof(rm.value_hash); hip_xmlrpc_getput(((mode & 0x00F0) | XMLRPC_MODE_RM), app, server, key, key_len, (char *)rm.value_hash, &value_hash_len, (char *)rm.secret, secret_len, rm_ttl); } return(retval); }
/* * \fn hip_dht_lookup_address() * * \param hit pointer to HIT for use with the lookup * \param addr pointer to sockaddr_storage for storing the returned * LOCATOR * \param retry if TRUE we will retry failed connection attempts * * \return Returns 0 on success, -1 on error. * * \brief Given a HIT, lookup an address using a DHT server. */ int hip_dht_lookup_address(hip_hit *hit, struct sockaddr *addr, int retry) { int mode, err, value_len; struct sockaddr_storage ss_server; struct sockaddr *server = (struct sockaddr*)&ss_server; __u8 dht_key[DHT_KEY_SIZE], hdrr[DHT_VAL_SIZE], *p_addr; int location, type, length, len, data_len, sig_verified = FALSE; hiphdr *hiph; tlv_head *tlv; locator *loc; hi_node *peer_hi = NULL; if (hip_dht_select_server(server) < 0) { return(-1); } /* * Prepare the DHT key: HIT_KEY (100 middle bits of HIT + padding) */ hit2hit_key(hit, dht_key); /* * For the Bamboo DHT (OpenDHT), this is tied * to an XML RPC "GET" call */ memset(hdrr, 0, DHT_VAL_SIZE); value_len = DHT_VAL_SIZE; mode = XMLRPC_MODE_GET; mode |= (retry) ? XMLRPC_MODE_RETRY_ON : XMLRPC_MODE_RETRY_OFF; err = hip_xmlrpc_getput(mode, XMLRPC_APP_ADDR, server, (char *)dht_key, DHT_KEY_SIZE, (char *)hdrr, &value_len, NULL, 0, 0); if (err < 0) { return(err); } if (parse_hdrr(hdrr, value_len) < 0) { return(-1); } /* * Validate the signature and grab the LOCATOR that matches * the address family provided in addr. */ location = 0; hiph = (hiphdr*) &hdrr[location]; data_len = location + ((hiph->hdr_len + 1) * 8); location += sizeof(hiphdr); while (location < data_len) { tlv = (tlv_head*) &hdrr[location]; type = ntohs(tlv->type); length = ntohs(tlv->length); /* first verify SIGNATURE */ if (!sig_verified && peer_hi && (type == PARAM_HIP_SIGNATURE)) { len = eight_byte_align(location); hiph->checksum = 0; hiph->hdr_len = (len / 8) - 1; if (validate_signature(hdrr, len, tlv, peer_hi->dsa, peer_hi->rsa) < 0) { log_(WARN, "HDRR has invalid signature.\n"); err = -1; break; } else { log_(NORM, "Signature in HDRR validated OK.\n"); } sig_verified = TRUE; location = sizeof(hiphdr); continue; } else if (!sig_verified && (type == PARAM_HOST_ID)) { if (handle_hi(&peer_hi, &hdrr[location]) < 0) { log_(WARN, "Error with HI from HDRR.\n"); err = -1; break; } if (!validate_hit(hiph->hit_sndr, peer_hi)) { log_(WARN, "HI in HDRR does not match the " "sender's HIT\n"); err = -1; break; } else { log_(NORM, "HI in HDRR validates the sender's " "HIT.\n"); } } if (type == PARAM_LOCATOR) { loc = ((tlv_locator*)tlv)->locator1; if ((loc->locator_type == LOCATOR_TYPE_IPV6) && (loc->locator_length == 4)) { p_addr = &loc->locator[0]; } else if ((loc->locator_type == LOCATOR_TYPE_SPI_IPV6) && (loc->locator_length == 5)) { p_addr = &loc->locator[4]; } else { log_(WARN, "HDRR has unknown LOCATOR type.\n"); err = -1; break; } if (IN6_IS_ADDR_V4MAPPED( (struct in6_addr*)p_addr)) { addr->sa_family = AF_INET; memcpy(SA2IP(addr), p_addr + 12, SAIPLEN(addr)); if (IN_MULTICAST(*(SA2IP(addr)))) { err = -1; break; } if (((struct sockaddr_in*)addr)->sin_addr. s_addr == INADDR_BROADCAST) { err = -1; break; } } else { unsigned char *p = SA2IP(addr); addr->sa_family = AF_INET6; memcpy(SA2IP(addr), p_addr, SAIPLEN(addr)); if (IN6_IS_ADDR_MULTICAST((struct in6_addr*)p)) { err = -1; break; } } log_(NORM, "Found peer address %s in HDRR\n", logaddr(addr)); err = 0; } location += tlv_length_to_parameter_length(length); } /* end while */ if (err < 0) { memset(addr, 0, sizeof(struct sockaddr_storage)); } if (peer_hi) { free_hi_node(peer_hi); } return(err); }
/* * \fn hip_dht_resolve_hi() * * \param hi pointer to host identity whose name, LSI, or HIT can be used * for lookups, and the HIT and address may be updated * \param retry if TRUE, we'll spawn a new thread an retry multiple times * without blocking * * \return returns -1 if there is a problem, 0 otherwise * * \brief Given a Host Identity, perform a DHT lookup using its HIT and store * any resulting address in the hi_node. If the HIT is missing, perform a HIT * lookup in the DHT using the name and/or LSI. */ int hip_dht_resolve_hi(hi_node *hi, int retry) { int err; struct sockaddr_storage ss_addr; struct sockaddr *addr = (struct sockaddr*) &ss_addr; sockaddr_list *list; char hit_str[INET6_ADDRSTRLEN]; #ifndef __WIN32__ pthread_attr_t attr; pthread_t thr; #endif if (hip_dht_select_server(addr) < 0) { return(0); /* prevents unneccessary thread creation */ } /* When retry is turned on, a separate thread will be forked that * will perform the DHT lookup(s), retry a certain number of times, * and exit */ if (retry == TRUE) { #ifdef __WIN32__ _beginthread(hip_dht_resolve_hi_thread, 0, (void *)hi); #else pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_create(&thr, &attr, hip_dht_resolve_hi_thread, hi); #endif return(0); /* We have been recursively called from a thread */ } else if (retry == 2) { retry = TRUE; /* used for calls below... */ } /* * First locate the HIT using the peer's name if this HIT is missing. */ if (hits_equal(hi->hit, zero_hit)) { if (hi->name_len == 0) { log_(NORM, "HIT and name not present, unable to perform" " DHT lookup.\n"); return(-1); } log_(NORM, "HIT not present for peer %s, performing DHT lookup " "using the name '%s'.\n", logaddr(SA(&hi->lsi)), hi->name); if ((err = hip_dht_lookup_hit_by_name(hi->name, &hi->hit, retry)) < 0) { /* no HIT from name, so we cannot do address lookup */ log_(WARN, "Unable to find HIT for %s in the DHT.\n", logaddr(SA(&hi->lsi))); return(err); } else { hit_to_str(hit_str, hi->hit); log_(NORM, "Discovered HIT for peer %s using the DHT: " "%s\n", hi->name, hit_str); } } /* * Look up current IP address using HIT as key */ memset(addr, 0, sizeof(struct sockaddr_storage)); addr->sa_family = AF_INET; if ((err = hip_dht_lookup_address(&hi->hit, addr, retry)) < 0) { return(err); } /* add address to list, checking if first item is empty */ pthread_mutex_lock(&hi->addrs_mutex); if ((hi->addrs.status == DELETED) || !VALID_FAM(&hi->addrs.addr)) { memcpy(&hi->addrs.addr, addr, SALEN(addr)); hi->addrs.if_index = 0; hi->addrs.lifetime = 0; hi->addrs.status = UNVERIFIED; hi->addrs.nonce = 0; gettimeofday(&hi->addrs.creation_time, NULL); } else { list = &hi->addrs; add_address_to_list(&list, addr, 0); } pthread_mutex_unlock(&hi->addrs_mutex); return(0); }
/* Add (flagadd=1) or remove (flagadd=0) userhost from the subscriber * database table. Comment is e.g. the subscriber from line or name. It * is added to the log. Event is the action type, e.g. "probe", * "manual", etc. The direction (sub/unsub) is inferred from * flagadd. Returns 1 on success, 0 on failure. If forcehash is >=0 it * is used in place of the calculated hash. This makes it possible to * add addresses with a hash that does not exist. forcehash has to be * 0..99. For unsubscribes, the address is only removed if forcehash * matches the actual hash. This way, ezmlm-manage can be prevented from * touching certain addresses that can only be removed by * ezmlm-unsub. Usually, this would be used for sublist addresses (to * avoid removal) and sublist aliases (to prevent users from subscribing * them (although the cookie mechanism would prevent the resulting * duplicate message from being distributed. */ int sub_sql_subscribe(struct subdbinfo *info, const char *table, const char *userhost, int flagadd, const char *comment, const char *event, int forcehash) { void *result; char *cpat; char szhash[3] = "00"; unsigned int j; unsigned char ch; int nparams; make_name(info,table?"_":0,table,0); /* lowercase and check address */ stralloc_copys(&addr,userhost); if (addr.len > 255) /* this is 401 in std ezmlm. 255 */ /* should be plenty! */ strerr_die2x(100,FATAL,MSG(ERR_ADDR_LONG)); j = byte_rchr(addr.s,addr.len,'@'); if (j == addr.len) strerr_die2x(100,FATAL,MSG(ERR_ADDR_AT)); cpat = addr.s + j; case_lowerb(cpat + 1,addr.len - j - 1); if (forcehash < 0) { stralloc_copy(&lcaddr,&addr); case_lowerb(lcaddr.s,j); /* make all-lc version of address */ ch = subhashsa(&lcaddr); } else ch = (forcehash % 100); szhash[0] = '0' + ch / 10; /* hash for sublist split */ szhash[1] = '0' + (ch % 10); if (flagadd) { /* FIXME: LOCK TABLES name WRITE */ stralloc_copys(&query,"SELECT address FROM "); stralloc_cat(&query,&name); stralloc_cats(&query," WHERE "); stralloc_cats(&query,sql_subscribe_select_where_defn); stralloc_copy(¶ms[0],&addr); result = sql_select(info,&query,1,params); if (sql_fetch_row(info,result,1,params)) { sql_free_result(info,result); /* FIXME: UNLOCK TABLES */ return 0; /* already subscribed */ } else { /* not there */ sql_free_result(info,result); stralloc_copys(&query,"INSERT INTO "); stralloc_cat(&query,&name); stralloc_cats(&query," (address,hash) VALUES "); stralloc_cats(&query,sql_subscribe_list_values_defn); stralloc_copy(¶ms[0],&addr); stralloc_copys(¶ms[1],szhash); sql_exec(info,&query,2,params); /* FIXME: UNLOCK TABLES */ } } else { /* unsub */ stralloc_copys(&query,"DELETE FROM "); stralloc_cat(&query,&name); stralloc_cats(&query," WHERE "); stralloc_copy(¶ms[0],&addr); if (forcehash >= 0) { stralloc_cats(&query,sql_subscribe_delete2_where_defn); stralloc_copys(¶ms[1],szhash); nparams = 2; } else { stralloc_cats(&query,sql_subscribe_delete1_where_defn); nparams = 1; } if (sql_exec(info,&query,1,params) == 0) return 0; /* address wasn't there*/ } /* log to subscriber log */ /* INSERT INTO t_slog (address,edir,etype,fromline) */ /* VALUES('address',{'+'|'-'},'etype','[comment]') */ stralloc_copys(&query,"INSERT INTO "); stralloc_cat(&query,&name); stralloc_cats(&query,"_slog (address,edir,etype,fromline) VALUES "); stralloc_cats(&query,sql_subscribe_slog_values_defn); stralloc_copy(¶ms[0],&addr); stralloc_copys(¶ms[1],flagadd?"+":"-"); /* edir */ stralloc_copyb(¶ms[2],event+1,!!*(event+1)); /* etype */ stralloc_copys(¶ms[3],comment && *comment ? comment : ""); /* from */ sql_exec(info,&query,4,params); /* log (ignore errors) */ stralloc_0(&addr); logaddr(table,event,addr.s,comment); /* also log to old log */ return 1; /* desired effect */ }
/* Add (flagadd=1) or remove (flagadd=0) userhost from the subscriber * database table. Comment is e.g. the subscriber from line or name. It * is added to the log. Event is the action type, e.g. "probe", * "manual", etc. The direction (sub/unsub) is inferred from * flagadd. Returns 1 on success, 0 on failure. If forcehash is >=0 it * is used in place of the calculated hash. This makes it possible to * add addresses with a hash that does not exist. forcehash has to be * 0..99. For unsubscribes, the address is only removed if forcehash * matches the actual hash. This way, ezmlm-manage can be prevented from * touching certain addresses that can only be removed by * ezmlm-unsub. Usually, this would be used for sublist addresses (to * avoid removal) and sublist aliases (to prevent users from subscribing * them (although the cookie mechanism would prevent the resulting * duplicate message from being distributed. */ static int _subscribe(struct subdbinfo *info, const char *table, const char *userhost, int flagadd, const char *comment, const char *event, int forcehash) { sqlite3_stmt *stmt; char *cpat; char szhash[3] = "00"; int res; unsigned int j; unsigned char ch; domain.len = 0; /* clear domain */ /* lowercase and check address */ if (!stralloc_copys(&addr,userhost)) die_nomem(); if (addr.len > 255) /* this is 401 in std ezmlm. 255 */ /* should be plenty! */ strerr_die2x(100,FATAL,MSG(ERR_ADDR_LONG)); j = byte_rchr(addr.s,addr.len,'@'); if (j == addr.len) strerr_die2x(100,FATAL,MSG(ERR_ADDR_AT)); cpat = addr.s + j; case_lowerb(cpat + 1,addr.len - j - 1); if (!stralloc_copy("ed, &addr)) die_nomem(); /* stored unescaped, so it should be ok if quoted.len is >255, as */ /* long as addr.len is not */ if (forcehash < 0) { if (!stralloc_copy(&lcaddr,&addr)) die_nomem(); case_lowerb(lcaddr.s,j); /* make all-lc version of address */ ch = subhashsa(&lcaddr); } else ch = (forcehash % 100); szhash[0] = '0' + ch / 10; /* hash for sublist split */ szhash[1] = '0' + (ch % 10); if (flagadd) { if (!stralloc_copys(&line,"SELECT address FROM ")) die_nomem(); if (!stralloc_cat_table(&line,info,table)) die_nomem(); if (!stralloc_cats(&line," WHERE address LIKE '")) die_nomem(); if (!stralloc_cat(&line,"ed)) die_nomem(); /* addr */ if (!stralloc_cats(&line,"'")) die_nomem(); if (!stralloc_0(&line)) die_nomem(); if ((stmt = _sqlquery(info, &line)) == NULL) strerr_die2x(111,FATAL,sqlite3_errmsg((sqlite3*)info->conn)); res = sqlite3_step(stmt); sqlite3_finalize(stmt); if (res == SQLITE_ROW) return 0; /* there */ else if (res != SQLITE_DONE) { strerr_die2x(111,FATAL,sqlite3_errmsg((sqlite3*)info->conn)); } else { /* not there */ if (!stralloc_copys(&line,"INSERT INTO ")) die_nomem(); if (!stralloc_cat_table(&line,info,table)) die_nomem(); if (!stralloc_cats(&line," (address,hash) VALUES ('")) die_nomem(); if (!stralloc_cat(&line,"ed)) die_nomem(); /* addr */ if (!stralloc_cats(&line,"',")) die_nomem(); if (!stralloc_cats(&line,szhash)) die_nomem(); /* hash */ if (!stralloc_cats(&line,")")) die_nomem(); if (!stralloc_0(&line)) die_nomem(); if ((stmt = _sqlquery(info, &line)) == NULL) strerr_die2x(111,FATAL,sqlite3_errmsg((sqlite3*)info->conn)); if (sqlite3_step(stmt) != SQLITE_DONE) strerr_die2x(111,FATAL,sqlite3_errmsg((sqlite3*)info->conn)); sqlite3_finalize(stmt); } } else { /* unsub */ if (!stralloc_copys(&line,"DELETE FROM ")) die_nomem(); if (!stralloc_cat_table(&line,info,table)) die_nomem(); if (!stralloc_cats(&line," WHERE address LIKE '")) die_nomem(); if (!stralloc_cat(&line,"ed)) die_nomem(); /* addr */ if (forcehash >= 0) { if (!stralloc_cats(&line,"' AND hash=")) die_nomem(); if (!stralloc_cats(&line,szhash)) die_nomem(); } else { if (!stralloc_cats(&line,"' AND hash BETWEEN 0 AND 52")) die_nomem(); } if (!stralloc_0(&line)) die_nomem(); if ((stmt = _sqlquery(info, &line)) == NULL) strerr_die2x(111,FATAL,sqlite3_errmsg((sqlite3*)info->conn)); if (sqlite3_step(stmt) != SQLITE_DONE) strerr_die2x(111,FATAL,sqlite3_errmsg((sqlite3*)info->conn)); sqlite3_finalize(stmt); if (sqlite3_changes((sqlite3*)info->conn) == 0) return 0; /* address wasn't there*/ } /* log to subscriber log */ /* INSERT INTO t_slog (address,edir,etype,fromline) */ /* VALUES('address',{'+'|'-'},'etype','[comment]') */ if (!stralloc_copys(&logline,"INSERT INTO ")) die_nomem(); if (!stralloc_cat_table(&logline,info,table)) die_nomem(); if (!stralloc_cats(&logline, "_slog (tai,address,edir,etype,fromline) VALUES (")) die_nomem(); if (!stralloc_catb(&logline,strnum,fmt_ulong(strnum,now()))) die_nomem(); if (!stralloc_cats(&logline,",'")) die_nomem(); if (!stralloc_cat(&logline,"ed)) die_nomem(); if (flagadd) { /* edir */ if (!stralloc_cats(&logline,"','+','")) die_nomem(); } else { if (!stralloc_cats(&logline,"','-','")) die_nomem(); } if (*(event + 1)) /* ezmlm-0.53 uses '' for ezmlm-manage's work */ if (!stralloc_catb(&logline,event+1,1)) die_nomem(); /* etype */ if (!stralloc_cats(&logline,"','")) die_nomem(); if (comment && *comment) { j = str_len(comment); if (!stralloc_copys("ed, comment)) die_nomem(); /* from */ if (!stralloc_cat(&logline,"ed)) die_nomem(); } if (!stralloc_cats(&logline,"')")) die_nomem(); if (!stralloc_0(&logline)) die_nomem(); if ((stmt = _sqlquery(info, &logline)) != NULL) { sqlite3_step(stmt); /* log (ignore errors) */ sqlite3_finalize(stmt); } if (!stralloc_0(&addr)) ; /* ignore errors */ logaddr(table,event,addr.s,comment); /* also log to old log */ return 1; /* desired effect */ }