static void incr_string(ACL_VSTRING *vp, int len, const char* s, int debug) { int i; printf("max: %ld, len: %ld, cnt: %ld\r\n", (long) vp->maxlen, vp->vbuf.len, vp->vbuf.cnt); for (i = 0; i < len; i++) ACL_VSTRING_ADDCH(vp, 'x'); if (s && *s) acl_vstring_sprintf_append(vp, "%s", s); else ACL_VSTRING_TERMINATE(vp); if (debug) printf("[%s]\r\n", acl_vstring_str(vp)); printf("strlen: %ld, ACL_VSTRING_LEN: %ld, max: %ld\r\n", (long) strlen(acl_vstring_str(vp)), (long) ACL_VSTRING_LEN(vp), (long) vp->maxlen); printf("Enter any key to continue ...\r\n\r\n"); getchar(); ACL_VSTRING_RESET(vp); ACL_VSTRING_TERMINATE(vp); }
void http_hdr_build_request(const HTTP_HDR_REQ *hdr_req, ACL_VSTRING *strbuf) { ACL_ARRAY *entries; HTTP_HDR_ENTRY *entry; int i, n; entries = hdr_req->hdr.entry_lnk; n = acl_array_size(entries); entry = (HTTP_HDR_ENTRY *) acl_array_index(entries, 0); #if 0 acl_vstring_sprintf(strbuf, "%s %s\r\n", entry->name, entry->value); #else acl_vstring_sprintf(strbuf, "%s %s HTTP/%d.%d\r\n", entry->name, acl_vstring_str(hdr_req->url_part), hdr_req->hdr.version.major, hdr_req->hdr.version.minor); #endif for (i = 1; i < n; i++) { entry = (HTTP_HDR_ENTRY *) acl_array_index(entries, i); if (entry == NULL) break; if (entry->off) continue; acl_vstring_sprintf_append(strbuf, "%s: %s\r\n", entry->name, entry->value); } acl_vstring_strcat(strbuf, "\r\n"); }
ACL_VSTRING *escape(ACL_VSTRING *result, const char *data, ssize_t len) { int ch; ACL_VSTRING_RESET(result); while (len-- > 0) { ch = *UCHAR(data++); if (ACL_ISASCII(ch)) { if (ACL_ISPRINT(ch)) { if (ch == '\\') ACL_VSTRING_ADDCH(result, ch); ACL_VSTRING_ADDCH(result, ch); continue; } else if (ch == '\a') { /* \a -> audible bell */ acl_vstring_strcat(result, "\\a"); continue; } else if (ch == '\b') { /* \b -> backspace */ acl_vstring_strcat(result, "\\b"); continue; } else if (ch == '\f') { /* \f -> formfeed */ acl_vstring_strcat(result, "\\f"); continue; } else if (ch == '\n') { /* \n -> newline */ acl_vstring_strcat(result, "\\n"); continue; } else if (ch == '\r') { /* \r -> carriagereturn */ acl_vstring_strcat(result, "\\r"); continue; } else if (ch == '\t') { /* \t -> horizontal tab */ acl_vstring_strcat(result, "\\t"); continue; } else if (ch == '\v') { /* \v -> vertical tab */ acl_vstring_strcat(result, "\\v"); continue; } } if (ACL_ISDIGIT(*UCHAR(data))) acl_vstring_sprintf_append(result, "\\%03d", ch); else acl_vstring_sprintf_append(result, "\\%d", ch); } ACL_VSTRING_TERMINATE(result); return (result); }
const char *str_name_mask_opt(ACL_VSTRING *buf, const char *context, const NAME_MASK *table, int mask, int flags) { const char *myname = "name_mask"; const NAME_MASK *np; int len; static ACL_VSTRING *my_buf = 0; int delim = (flags & NAME_MASK_COMMA ? ',' : (flags & NAME_MASK_PIPE ? '|' : ' ')); if (buf == 0) { if (my_buf == 0) my_buf = acl_vstring_alloc(1); buf = my_buf; } ACL_VSTRING_RESET(buf); for (np = table; mask != 0; np++) { if (np->name == 0) { if (flags & NAME_MASK_FATAL) { acl_msg_fatal("%s: unknown %s bit in mask: 0x%x", myname, context, mask); } else if (flags & NAME_MASK_RETURN) { acl_msg_warn("%s: unknown %s bit in mask: 0x%x", myname, context, mask); return (0); } else if (flags & NAME_MASK_NUMBER) { acl_vstring_sprintf_append(buf, "0x%x%c", mask, delim); } break; } if (mask & np->mask) { mask &= ~np->mask; acl_vstring_sprintf_append(buf, "%s%c", np->name, delim); } } if ((len = (int) ACL_VSTRING_LEN(buf)) > 0) acl_vstring_truncate(buf, len - 1); ACL_VSTRING_TERMINATE(buf); return (STR(buf)); }
void http_hdr_build(const HTTP_HDR *hdr, ACL_VSTRING *strbuf) { ACL_ARRAY *entries; HTTP_HDR_ENTRY *entry; int i, n; entries = hdr->entry_lnk; n = acl_array_size(entries); entry = (HTTP_HDR_ENTRY *) acl_array_index(entries, 0); acl_vstring_sprintf(strbuf, "%s %s\r\n", entry->name, entry->value); for (i = 1; i < n; i++) { entry = (HTTP_HDR_ENTRY *) acl_array_index(entries, i); if (entry == NULL) break; if (entry->off) continue; acl_vstring_sprintf_append(strbuf, "%s: %s\r\n", entry->name, entry->value); } acl_vstring_strcat(strbuf, "\r\n"); }
static void __add_cookie_item(ACL_HTABLE *table, const char *data) { /* data format: name=value */ const char *myname = "__add_cookie_item"; ACL_ARGV *argv = NULL; ACL_VSTRING *str = NULL; const char *name; char *value; char *ptr; int i; #undef RETURN #define RETURN do { \ if (argv) \ acl_argv_free(argv); \ return; \ } while(0); #undef TRUNC_BLANK #define TRUNC_BLANK(_x_) do { \ char *_ptr_; \ while(*_x_ == ' ' || *_x_ == '\t') \ _x_++; \ if (*_x_ == 0) \ RETURN; \ _ptr_ = _x_; \ while (*_ptr_) { \ if (*_ptr_ == ' ' || *_ptr_ == '\t') { \ *_ptr_ = 0; \ break; \ } \ _ptr_++; \ } \ } while (0); #undef TRUNC_BLANK_NORETURN #define TRUNC_BLANK_NORETURN(_x_) do { \ char *_ptr_; \ while(*_x_ == ' ' || *_x_ == '\t') \ _x_++; \ _ptr_ = _x_; \ while (*_ptr_) { \ if (*_ptr_ == ' ' || *_ptr_ == '\t') { \ *_ptr_ = 0; \ break; \ } \ _ptr_++; \ } \ } while (0); argv = acl_argv_split(data, "="); if (argv->argc < 2) /* data: "name" or "name="*/ RETURN; ptr = acl_argv_index(argv, 0); TRUNC_BLANK(ptr); name = ptr; /* 有些站点的COOKIE比较弱,如和讯的reg.hexun.com,COOKIE名会有重复 * 的情况,所以必须判断一下,不必重复存储相同名字的COOKIE值,即如果 * 出现重复COOKIE名,则只存储第一个,这样就避免了采用哈希方式存储的 * 漏内存的现象发生。--- zsx, 2008.1.8 */ if (acl_htable_find(table, name) != NULL) { RETURN; } str = acl_vstring_alloc(256); for (i = 1; i < argv->argc; i++) { ptr = acl_argv_index(argv, i); if (ptr == NULL) break; TRUNC_BLANK_NORETURN(ptr); if (*ptr == 0) continue; if (i == 1) acl_vstring_sprintf_append(str, "%s", ptr); else acl_vstring_sprintf_append(str, "=%s", ptr); } /* 将真实的存储数据的区域内存引出, 同时将外包结构内存释放, * POSTFIX真是个好东西:) ---zsx */ value = acl_vstring_export(str); if (acl_htable_enter(table, name, value) == NULL) acl_msg_fatal("%s, %s(%d): acl_htable_enter error=%s", __FILE__, myname, __LINE__, acl_last_serror()); RETURN; }
static int __log_open(const char *filename, void *ctx) { LOG_WRAP *h_log = (LOG_WRAP *) ctx; int logme = 0; char *facility_name = NULL; E_LOG_PRIORITY_T priority = E_LOG_INFO; E_LOG_ACTION_T action = E_LOG_PER_DAY; int flush = 1; size_t limit_size = 0; E_LOG_SYNC_ACTION_T sync_action = E_LOG_SEM_WITH_MT; char *sem_name = NULL; char *ptr, *pname; ACL_ARGV *env_argv; ACL_VSTRING *log_buf; int i; if (filename == NULL || *filename == 0) return (-1); acl_snprintf(h_log->filename, sizeof(h_log->filename), "%s", filename); /* env: facility:x, priority:x, action:x, flush:x, limit_size:x, sync_action:x, sem_name:x */ ptr = getenv("SERVICE_ENV"); if (ptr == NULL) return (-1); env_argv = acl_argv_split(ptr, ",\t "); if (env_argv == NULL) return (-1); if (env_argv->argc == 0) { acl_argv_free(env_argv); return (-1); } log_buf = acl_vstring_alloc(256); for (i = 0; i < env_argv->argc; i++) { pname = acl_argv_index(env_argv, i); ptr = strchr(pname, ':'); if (ptr == NULL) continue; *ptr++ = 0; if (*ptr == 0) continue; if (i == 0) acl_vstring_sprintf(log_buf, "%s:%s", pname, ptr); else acl_vstring_sprintf_append(log_buf, ", %s:%s", pname, ptr); if (strcasecmp(pname, "logme") == 0) { if (strcasecmp(ptr, "TRUE") == 0) logme = 1; } else if (strcasecmp(pname, "facility") == 0) { facility_name = ptr; } else if (strcasecmp(pname, "priority") == 0) { if (strcasecmp(ptr, "E_LOG_NOLOG") == 0) priority = E_LOG_NOLOG; else if (strcasecmp(ptr, "E_LOG_EMERG") == 0) priority = E_LOG_EMERG; else if (strcasecmp(ptr, "E_LOG_ALERT") == 0) priority = E_LOG_ALERT; else if (strcasecmp(ptr, "E_LOG_CRIT") == 0) priority = E_LOG_CRIT; else if (strcasecmp(ptr, "E_LOG_ERR") == 0) priority = E_LOG_ERR; else if (strcasecmp(ptr, "E_LOG_WARNING") == 0) priority = E_LOG_WARNING; else if (strcasecmp(ptr, "E_LOG_NOTICE") == 0) priority = E_LOG_NOTICE; else if (strcasecmp(ptr, "E_LOG_INFO") == 0) priority = E_LOG_INFO; else if (strcasecmp(ptr, "E_LOG_DEBUG") == 0) priority = E_LOG_DEBUG; } else if (strcasecmp(pname, "action") == 0) { if (strcasecmp(ptr, "E_LOG_PER_HOUR") == 0) action = E_LOG_PER_HOUR; else if (strcasecmp(ptr, "E_LOG_PER_DAY") == 0) action = E_LOG_PER_DAY; else if (strcasecmp(ptr, "E_LOG_PER_WEEK") == 0) action = E_LOG_PER_WEEK; else if (strcasecmp(ptr, "E_LOG_PER_MONTH") == 0) action = E_LOG_PER_MONTH; else if (strcasecmp(ptr, "E_LOG_PER_YEAR") == 0) action = E_LOG_PER_YEAR; else if (strcasecmp(ptr, "E_LOG_LIMIT_SIZE") == 0) action = E_LOG_LIMIT_SIZE; else if (strcasecmp(ptr, "E_LOG_SYSLOG") == 0) action = E_LOG_SYSLOG; } else if (strcasecmp(pname, "flush") == 0) { if (strcasecmp(ptr, "sync_flush") == 0) flush = 1; else if (strcasecmp(ptr, "async_flush") == 0) flush = 0; } else if (strcasecmp(pname, "limit_size") == 0) { limit_size = atoi(ptr); } else if (strcasecmp(pname, "sync_action") == 0) { if (strcasecmp(ptr, "E_LOG_NO_SYNC") == 0) sync_action = E_LOG_NO_SYNC; else if (strcasecmp(ptr, "E_LOG_THREAD_MUTEX") == 0) sync_action = E_LOG_THREAD_MUTEX; else if (strcasecmp(ptr, "E_LOG_FILE_LOCK") == 0) sync_action = E_LOG_FILE_LOCK; else if (strcasecmp(ptr, "E_LOG_SEM_WITH_MT") == 0) sync_action = E_LOG_SEM_WITH_MT; else if (strcasecmp(ptr, "E_LOG_FILE_APPEND_WITH_MT") == 0) sync_action = E_LOG_FILE_APPEND_WITH_MT; } else if (strcasecmp(pname, "sem_name") == 0) { sem_name = ptr; } } #if 0 LC_SysLogCreate(&h_log->h_log, h_log->filename); #endif if (action == E_LOG_LIMIT_SIZE) { if (limit_size == 0) limit_size = 512; /* set default size: 512 MB */ } else limit_size = 0; if (sync_action == E_LOG_SEM_WITH_MT) { if (sem_name == NULL || *sem_name == 0) { sem_name = acl_concatenate("/tmp/", acl_safe_basename(filename), ".sem", NULL); } else sem_name = acl_mystrdup(sem_name); } else if (sem_name) { sem_name = NULL; } h_log->h_log = e_log_new2(h_log->filename, priority, action, flush, limit_size, facility_name, sync_action, sem_name); if (sem_name) acl_myfree(sem_name); if (logme) { /* char cmd[1024], buf[512]; snprintf(buf, sizeof(buf), "filename=%s, priority=%d, action=%d, flush=%d, " "limit_size=%d, facility_name=%s, sync_action=%d, sem_name=%s", h_log->filename, priority, action, flush, limit_size, facility_name, sync_action, sem_name); snprintf(cmd, sizeof(cmd), "echo '%s, buf(%s)' >> /tmp/test1.log", acl_vstring_str(log_buf), buf); system(cmd); */ e_log2(h_log->h_log, "master_env: %s", acl_vstring_str(log_buf)); } e_log2(h_log->h_log, "filename=%s, priority=%d, action=%d, flush=%d, " "limit_size=%d, facility_name=%s, sync_action=%d, sem_name=%s", h_log->filename, priority, action, flush, limit_size, facility_name, sync_action, sem_name); if (log_buf) acl_vstring_free(log_buf); return (0); }
/* * This is the actual startup routine for the connection. We expect that the * buffers are flushed and the "220 Ready to start TLS" was received by us, * so that we can immediately start the TLS handshake process. */ TLS_SESS_STATE *tls_client_start(const TLS_CLIENT_START_PROPS *props) { const char *myname = "tls_client_start"; int sts; int protomask; const char *cipher_list; SSL_SESSION *session; SSL_CIPHER *cipher; X509 *peercert; TLS_SESS_STATE *TLScontext; TLS_APPL_STATE *app_ctx = props->ctx; ACL_VSTRING *myserverid; if (props->log_level >= 1) acl_msg_info("%s(%d): setting up TLS connection to %s", myname, __LINE__, props->namaddr); /* * First make sure we have valid protocol and cipher parameters * * The cipherlist will be applied to the global SSL context, where it can be * repeatedly reset if necessary, but the protocol restrictions will be * is applied to the SSL connection, because protocol restrictions in the * global context cannot be cleared. */ /* * OpenSSL will ignore cached sessions that use the wrong protocol. So we * do not need to filter out cached sessions with the "wrong" protocol, * rather OpenSSL will simply negotiate a new session. * * Still, we salt the session lookup key with the protocol list, so that * sessions found in the cache are always acceptable. */ protomask = tls_protocol_mask(props->protocols); if (protomask == TLS_PROTOCOL_INVALID) { /* tls_protocol_mask() logs no warning. */ acl_msg_warn("%s(%d): nameaddr: %s: Invalid TLS protocol list \"%s\": aborting TLS session", myname, __LINE__, props->namaddr, props->protocols); return (0); } myserverid = acl_vstring_alloc(100); acl_vstring_sprintf_append(myserverid, "%s&p=%d", props->serverid, protomask); /* * Per session cipher selection for sessions with mandatory encryption * * By the time a TLS client is negotiating ciphers it has already offered to * re-use a session, it is too late to renege on the offer. So we must * not attempt to re-use sessions whose ciphers are too weak. We salt the * session lookup key with the cipher list, so that sessions found in the * cache are always acceptable. */ cipher_list = tls_set_ciphers(app_ctx, "TLS", props->cipher_grade, props->cipher_exclusions); if (cipher_list == 0) { acl_msg_warn("%s(%d): %s: %s: aborting TLS session", myname, __LINE__, props->namaddr, acl_vstring_str(app_ctx->why)); acl_vstring_free(myserverid); return (0); } if (props->log_level >= 2) acl_msg_info("%s(%d): %s: TLS cipher list \"%s\"", myname, __LINE__, props->namaddr, cipher_list); acl_vstring_sprintf_append(myserverid, "&c=%s", cipher_list); /* * Allocate a new TLScontext for the new connection and get an SSL * structure. Add the location of TLScontext to the SSL to later retrieve * the information inside the tls_verify_certificate_callback(). * * If session caching was enabled when TLS was initialized, the cache type * is stored in the client SSL context. */ TLScontext = tls_alloc_sess_context(props->log_level, props->namaddr); TLScontext->cache_type = app_ctx->cache_type; TLScontext->serverid = acl_vstring_export(myserverid); if ((TLScontext->con = SSL_new(app_ctx->ssl_ctx)) == NULL) { acl_msg_warn("%s(%d): Could not allocate 'TLScontext->con' with SSL_new()", myname, __LINE__); tls_print_errors(); tls_free_context(TLScontext); return (0); } if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) { acl_msg_warn("%s(%d): Could not set application data for 'TLScontext->con'", myname, __LINE__); tls_print_errors(); tls_free_context(TLScontext); return (0); } /* * Apply session protocol restrictions. */ if (protomask != 0) SSL_set_options(TLScontext->con, ((protomask & TLS_PROTOCOL_TLSv1) ? SSL_OP_NO_TLSv1 : 0L) | ((protomask & TLS_PROTOCOL_SSLv3) ? SSL_OP_NO_SSLv3 : 0L) | ((protomask & TLS_PROTOCOL_SSLv2) ? SSL_OP_NO_SSLv2 : 0L)); /* * The TLS connection is realized by a BIO_pair, so obtain the pair. * * XXX There is no need to make internal_bio a member of the TLScontext * structure. It will be attached to TLScontext->con, and destroyed along * with it. The network_bio, however, needs to be freed explicitly. */ if (!BIO_new_bio_pair(&TLScontext->internal_bio, TLS_BIO_BUFSIZE, &TLScontext->network_bio, TLS_BIO_BUFSIZE)) { acl_msg_warn("%s(%d): Could not obtain BIO_pair", myname, __LINE__); tls_print_errors(); tls_free_context(TLScontext); return (0); } /* * XXX To avoid memory leaks we must always call SSL_SESSION_free() after * calling SSL_set_session(), regardless of whether or not the session * will be reused. */ if (TLScontext->cache_type) { session = load_clnt_session(TLScontext); if (session) { SSL_set_session(TLScontext->con, session); SSL_SESSION_free(session); /* 200411 */ #if (OPENSSL_VERSION_NUMBER < 0x00906011L) || (OPENSSL_VERSION_NUMBER == 0x00907000L) /* * Ugly Hack: OpenSSL before 0.9.6a does not store the verify * result in sessions for the client side. We modify the session * directly which is version specific, but this bug is version * specific, too. * * READ: 0-09-06-01-1 = 0-9-6-a-beta1: all versions before beta1 * have this bug, it has been fixed during development of 0.9.6a. * The development version of 0.9.7 can have this bug, too. It * has been fixed on 2000/11/29. */ SSL_set_verify_result(TLScontext->con, session->verify_result); #endif } } /* * Before really starting anything, try to seed the PRNG a little bit * more. */ tls_int_seed(); if (var_tls_daemon_rand_bytes > 0) (void) tls_ext_seed(var_tls_daemon_rand_bytes); /* * Initialize the SSL connection to connect state. This should not be * necessary anymore since 0.9.3, but the call is still in the library * and maintaining compatibility never hurts. */ SSL_set_connect_state(TLScontext->con); /* * Connect the SSL connection with the Postfix side of the BIO-pair for * reading and writing. */ SSL_set_bio(TLScontext->con, TLScontext->internal_bio, TLScontext->internal_bio); /* * If the debug level selected is high enough, all of the data is dumped: * 3 will dump the SSL negotiation, 4 will dump everything. * * We do have an SSL_set_fd() and now suddenly a BIO_ routine is called? * Well there is a BIO below the SSL routines that is automatically * created for us, so we can use it for debugging purposes. */ if (props->log_level >= 3) BIO_set_callback(SSL_get_rbio(TLScontext->con), tls_bio_dump_cb); /* * Start TLS negotiations. This process is a black box that invokes our * call-backs for certificate verification. * * Error handling: If the SSL handhake fails, we print out an error message * and remove all TLS state concerning this session. */ sts = tls_bio_connect(ACL_VSTREAM_SOCK(props->stream), props->timeout, TLScontext); if (sts <= 0) { acl_msg_info("%s(%d): SSL_connect error to %s: %d", myname, __LINE__, props->namaddr, sts); tls_print_errors(); uncache_session(app_ctx->ssl_ctx, TLScontext); tls_free_context(TLScontext); return (0); } /* Only log_level==4 dumps everything */ if (props->log_level < 4) BIO_set_callback(SSL_get_rbio(TLScontext->con), 0); /* * The caller may want to know if this session was reused or if a new * session was negotiated. */ TLScontext->session_reused = SSL_session_reused(TLScontext->con); if (props->log_level >= 2 && TLScontext->session_reused) acl_msg_info("%s(%d): %s: Reusing old session", myname, __LINE__, TLScontext->namaddr); /* * Do peername verification if requested and extract useful information * from the certificate for later use. */ if ((peercert = SSL_get_peer_certificate(TLScontext->con)) != 0) { TLScontext->peer_status |= TLS_CERT_FLAG_PRESENT; /* * Peer name or fingerprint verification as requested. * Unconditionally set peer_CN, issuer_CN and peer_fingerprint. */ verify_extract_name(TLScontext, peercert, props); verify_extract_print(TLScontext, peercert, props); X509_free(peercert); } else { TLScontext->issuer_CN = acl_mystrdup(""); TLScontext->peer_CN = acl_mystrdup(""); TLScontext->peer_fingerprint = acl_mystrdup(""); } /* * Finally, collect information about protocol and cipher for logging */ TLScontext->protocol = SSL_get_version(TLScontext->con); cipher = SSL_get_current_cipher(TLScontext->con); TLScontext->cipher_name = SSL_CIPHER_get_name(cipher); TLScontext->cipher_usebits = SSL_CIPHER_get_bits(cipher, &(TLScontext->cipher_algbits)); /* * The TLS engine is active. Switch to the tls_timed_read/write() * functions and make the TLScontext available to those functions. */ tls_stream_start(props->stream, TLScontext); /* * All the key facts in a single log entry. */ if (props->log_level >= 1) acl_msg_info("%s(%d): %s TLS connection established to %s: %s with cipher %s " "(%d/%d bits)", myname, __LINE__, TLS_CERT_IS_MATCHED(TLScontext) ? "Verified" : TLS_CERT_IS_TRUSTED(TLScontext) ? "Trusted" : "Untrusted", props->namaddr, TLScontext->protocol, TLScontext->cipher_name, TLScontext->cipher_usebits, TLScontext->cipher_algbits); tls_int_seed(); return (TLScontext); }