/* * SendQuery: send the query string to the backend * (and print out results) * * Note: This is the "front door" way to send a query. That is, use it to * send queries actually entered by the user. These queries will be subject to * single step mode. * To send "back door" queries (generated by slash commands, etc.) in a * controlled way, use PSQLexec(). * * Returns true if the query executed successfully, false otherwise. */ bool SendQuery(const char *query) { PGresult *results; TimevalStruct before, after; bool OK; if (!pset.db) { psql_error("You are currently not connected to a database.\n"); return false; } if (GetVariableBool(pset.vars, "SINGLESTEP")) { char buf[3]; printf(gettext("***(Single step mode: verify command)*******************************************\n" "%s\n" "***(press return to proceed or enter x and return to cancel)********************\n"), query); fflush(stdout); if (fgets(buf, sizeof(buf), stdin) != NULL) if (buf[0] == 'x') return false; } else if (VariableEquals(pset.vars, "ECHO", "queries")) { puts(query); fflush(stdout); } SetCancelConn(); if (PQtransactionStatus(pset.db) == PQTRANS_IDLE && !GetVariableBool(pset.vars, "AUTOCOMMIT") && !is_transact_command(query)) { results = PQexec(pset.db, "BEGIN"); if (PQresultStatus(results) != PGRES_COMMAND_OK) { psql_error("%s", PQerrorMessage(pset.db)); PQclear(results); ResetCancelConn(); return false; } PQclear(results); } if (pset.timing) GETTIMEOFDAY(&before); results = PQexec(pset.db, query); /* these operations are included in the timing result: */ OK = (AcceptResult(results) && ProcessCopyResult(results)); if (pset.timing) GETTIMEOFDAY(&after); /* but printing results isn't: */ if (OK) OK = PrintQueryResults(results); PQclear(results); /* Possible microtiming output */ if (OK && pset.timing) printf(gettext("Time: %.3f ms\n"), DIFF_MSEC(&after, &before)); /* check for events that may occur during query execution */ if (pset.encoding != PQclientEncoding(pset.db) && PQclientEncoding(pset.db) >= 0) { /* track effects of SET CLIENT_ENCODING */ pset.encoding = PQclientEncoding(pset.db); pset.popt.topt.encoding = pset.encoding; SetVariable(pset.vars, "ENCODING", pg_encoding_to_char(pset.encoding)); } PrintNotifications(); return OK; }
/* * hourly_stats - print some interesting stats */ void write_stats(void) { FILE *fp; double ftemp; #ifdef DOSYNCTODR struct timeval tv; #if !defined(VMS) int prio_set; #endif #ifdef HAVE_GETCLOCK struct timespec ts; #endif int o_prio; /* * Sometimes having a Sun can be a drag. * * The kernel variable dosynctodr controls whether the system's * soft clock is kept in sync with the battery clock. If it * is zero, then the soft clock is not synced, and the battery * clock is simply left to rot. That means that when the system * reboots, the battery clock (which has probably gone wacky) * sets the soft clock. That means ntpd starts off with a very * confused idea of what time it is. It then takes a large * amount of time to figure out just how wacky the battery clock * has made things drift, etc, etc. The solution is to make the * battery clock sync up to system time. The way to do THAT is * to simply set the time of day to the current time of day, but * as quickly as possible. This may, or may not be a sensible * thing to do. * * CAVEAT: settimeofday() steps the sun clock by about 800 us, * so setting DOSYNCTODR seems a bad idea in the * case of us resolution */ #if !defined(VMS) /* * (prr) getpriority returns -1 on error, but -1 is also a valid * return value (!), so instead we have to zero errno before the * call and check it for non-zero afterwards. */ errno = 0; prio_set = 0; o_prio = getpriority(PRIO_PROCESS,0); /* Save setting */ /* * (prr) if getpriority succeeded, call setpriority to raise * scheduling priority as high as possible. If that succeeds * as well, set the prio_set flag so we remember to reset * priority to its previous value below. Note that on Solaris * 2.6 (and beyond?), both getpriority and setpriority will fail * with ESRCH, because sched_setscheduler (called from main) put * us in the real-time scheduling class which setpriority * doesn't know about. Being in the real-time class is better * than anything setpriority can do, anyhow, so this error is * silently ignored. */ if ((errno == 0) && (setpriority(PRIO_PROCESS,0,-20) == 0)) prio_set = 1; /* overdrive */ #endif /* VMS */ #ifdef HAVE_GETCLOCK (void) getclock(TIMEOFDAY, &ts); tv.tv_sec = ts.tv_sec; tv.tv_usec = ts.tv_nsec / 1000; #else /* not HAVE_GETCLOCK */ GETTIMEOFDAY(&tv,(struct timezone *)NULL); #endif /* not HAVE_GETCLOCK */ if (ntp_set_tod(&tv,(struct timezone *)NULL) != 0) msyslog(LOG_ERR, "can't sync battery time: %m"); #if !defined(VMS) if (prio_set) setpriority(PRIO_PROCESS, 0, o_prio); /* downshift */ #endif /* VMS */ #endif /* DOSYNCTODR */ record_sys_stats(); ftemp = fabs(prev_drift_comp - drift_comp); prev_drift_comp = drift_comp; if (ftemp > clock_phi) return; if (stats_drift_file != 0 && drift_file_sw) { /* * When the frequency file is written, initialize the * wander threshold to a configured initial value. * Thereafter reduce it by a factor of 0.5. When it * drops below the frequency wander, write the frequency * file. This adapts to the prevailing wander yet * minimizes the file writes. */ drift_file_sw = FALSE; wander_resid *= 0.5; #ifdef DEBUG if (debug) printf("write_stats: wander %.6lf thresh %.6lf, freq %.6lf\n", clock_stability * 1e6, wander_resid * 1e6, drift_comp * 1e6); #endif if (sys_leap != LEAP_NOTINSYNC && clock_stability > wander_resid) { wander_resid = wander_threshold; if ((fp = fopen(stats_temp_file, "w")) == NULL) { msyslog(LOG_ERR, "frequency file %s: %m", stats_temp_file); return; } fprintf(fp, "%.3f\n", drift_comp * 1e6); (void)fclose(fp); /* atomic */ #ifdef SYS_WINNT if (_unlink(stats_drift_file)) /* rename semantics differ under NT */ msyslog(LOG_WARNING, "Unable to remove prior drift file %s, %m", stats_drift_file); #endif /* SYS_WINNT */ #ifndef NO_RENAME if (rename(stats_temp_file, stats_drift_file)) msyslog(LOG_WARNING, "Unable to rename temp drift file %s to %s, %m", stats_temp_file, stats_drift_file); #else /* we have no rename NFS of ftp in use */ if ((fp = fopen(stats_drift_file, "w")) == NULL) { msyslog(LOG_ERR, "frequency file %s: %m", stats_drift_file); return; } #endif #if defined(VMS) /* PURGE */ { $DESCRIPTOR(oldvers,";-1"); struct dsc$descriptor driftdsc = { strlen(stats_drift_file), 0, 0, stats_drift_file }; while(lib$delete_file(&oldvers, &driftdsc) & 1); } #endif } else { /* XXX: Log a message at INFO level */ } } }
static void tlsmgr_pre_init(char *unused_name, char **unused_argv) { char *path; struct timeval tv; TLSMGR_SCACHE *ent; VSTRING *redirect; HTABLE *dup_filter; const char *dup_label; /* * If nothing else works then at least this will get us a few bits of * entropy. * * XXX This is our first call into the OpenSSL library. We should find out * if this can be moved to the post-jail initialization phase, without * breaking compatibility with existing installations. */ GETTIMEOFDAY(&tv); tv.tv_sec ^= getpid(); RAND_seed(&tv, sizeof(struct timeval)); /* * Open the external entropy source. We will not be able to open it again * after we are sent to chroot jail, so we keep it open. Errors are not * fatal. The exchange file (see below) is the only entropy source that * really matters in the long run. * * Security note: we open the entropy source while privileged, but we don't * access the source until after we release privileges. This way, none of * the OpenSSL code gets to execute while we are privileged. */ if (*var_tls_rand_source) { /* * Source is a random device. */ if (!strncmp(var_tls_rand_source, DEV_PREF, DEV_PREF_LEN)) { path = DEV_PATH(var_tls_rand_source); rand_source_dev = tls_prng_dev_open(path, TLS_MGR_TIMEOUT); if (rand_source_dev == 0) msg_warn("cannot open entropy device %s: %m", path); } /* * Source is an EGD compatible socket. */ else if (!strncmp(var_tls_rand_source, EGD_PREF, EGD_PREF_LEN)) { path = EGD_PATH(var_tls_rand_source); rand_source_egd = tls_prng_egd_open(path, TLS_MGR_TIMEOUT); if (rand_source_egd == 0) msg_warn("cannot connect to EGD server %s: %m", path); } /* * Source is regular file. We read this only once. */ else { rand_source_file = tls_prng_file_open(var_tls_rand_source, TLS_MGR_TIMEOUT); } } else { msg_warn("no entropy source specified with parameter %s", VAR_TLS_RAND_SOURCE); msg_warn("encryption keys etc. may be predictable"); } /* * Security: don't create root-owned files that contain untrusted data. * And don't create Postfix-owned files in root-owned directories, * either. We want a correct relationship between (file/directory) * ownership and (file/directory) content. */ SAVE_AND_SET_EUGID(var_owner_uid, var_owner_gid); redirect = vstring_alloc(100); /* * Open the PRNG exchange file before going to jail, but don't use root * privileges. Start the exchange file read/update pseudo thread after * dropping privileges. */ if (*var_tls_rand_exch_name) { rand_exch = tls_prng_exch_open(data_redirect_file(redirect, var_tls_rand_exch_name)); if (rand_exch == 0) msg_fatal("cannot open PRNG exchange file %s: %m", var_tls_rand_exch_name); } /* * Open the session cache files and discard old information before going * to jail, but don't use root privilege. Start the cache maintenance * pseudo threads after dropping privileges. */ dup_filter = htable_create(sizeof(cache_table) / sizeof(cache_table[0])); for (ent = cache_table; ent->cache_label; ++ent) { /* Sanitize session timeout */ if (*ent->cache_timeout > 0) { if (*ent->cache_timeout < TLS_SESSION_LIFEMIN) *ent->cache_timeout = TLS_SESSION_LIFEMIN; } else { *ent->cache_timeout = 0; } /* External cache database disabled if timeout is non-positive */ if (*ent->cache_timeout > 0 && **ent->cache_db) { if ((dup_label = htable_find(dup_filter, *ent->cache_db)) != 0) msg_fatal("do not use the same TLS cache file %s for %s and %s", *ent->cache_db, dup_label, ent->cache_label); htable_enter(dup_filter, *ent->cache_db, ent->cache_label); ent->cache_info = tls_scache_open(data_redirect_map(redirect, *ent->cache_db), ent->cache_label, tls_log_mask(ent->log_param, *ent->log_level) & TLS_LOG_CACHE, *ent->cache_timeout); } } htable_free(dup_filter, (void (*) (char *)) 0); /* * Clean up and restore privilege. */ vstring_free(redirect); RESTORE_SAVED_EUGID(); }
int main(int argc, char **argv) { struct stat st; int fd; int c; VSTRING *buf; int status; MAIL_STREAM *dst; int rec_type; static char *segment_info[] = { REC_TYPE_POST_ENVELOPE, REC_TYPE_POST_CONTENT, REC_TYPE_POST_EXTRACT, "" }; char **expected; uid_t uid = getuid(); ARGV *import_env; const char *error_text; char *attr_name; char *attr_value; const char *errstr; char *junk; struct timeval start; int saved_errno; int from_count = 0; int rcpt_count = 0; int validate_input = 1; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; /* * Be consistent with file permissions. */ umask(022); /* * To minimize confusion, make sure that the standard file descriptors * are open before opening anything else. XXX Work around for 44BSD where * fstat can return EBADF on an open file descriptor. */ for (fd = 0; fd < 3; fd++) if (fstat(fd, &st) == -1 && (close(fd), open("/dev/null", O_RDWR, 0)) != fd) msg_fatal("open /dev/null: %m"); /* * Set up logging. Censor the process name: it is provided by the user. */ argv[0] = "postdrop"; msg_vstream_init(argv[0], VSTREAM_ERR); msg_syslog_init(mail_task("postdrop"), LOG_PID, LOG_FACILITY); set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0])); /* * Check the Postfix library version as soon as we enable logging. */ MAIL_VERSION_CHECK; /* * Parse JCL. This program is set-gid and must sanitize all command-line * arguments. The configuration directory argument is validated by the * mail configuration read routine. Don't do complex things until we have * completed initializations. */ while ((c = GETOPT(argc, argv, "c:rv")) > 0) { switch (c) { case 'c': if (setenv(CONF_ENV_PATH, optarg, 1) < 0) msg_fatal("out of memory"); break; case 'r': /* forward compatibility */ break; case 'v': if (geteuid() == 0) msg_verbose++; break; default: msg_fatal("usage: %s [-c config_dir] [-v]", argv[0]); } } /* * Read the global configuration file and extract configuration * information. Some claim that the user should supply the working * directory instead. That might be OK, given that this command needs * write permission in a subdirectory called "maildrop". However we still * need to reliably detect incomplete input, and so we must perform * record-level I/O. With that, we should also take the opportunity to * perform some sanity checks on the input. */ mail_conf_read(); /* Re-evaluate mail_task() after reading main.cf. */ msg_syslog_init(mail_task("postdrop"), LOG_PID, LOG_FACILITY); get_mail_conf_str_table(str_table); /* * Mail submission access control. Should this be in the user-land gate, * or in the daemon process? */ mail_dict_init(); if ((errstr = check_user_acl_byuid(VAR_SUBMIT_ACL, var_submit_acl, uid)) != 0) msg_fatal("User %s(%ld) is not allowed to submit mail", errstr, (long) uid); /* * Stop run-away process accidents by limiting the queue file size. This * is not a defense against DOS attack. */ if (var_message_limit > 0 && get_file_limit() > var_message_limit) set_file_limit((off_t) var_message_limit); /* * This program is installed with setgid privileges. Strip the process * environment so that we don't have to trust the C library. */ import_env = mail_parm_split(VAR_IMPORT_ENVIRON, var_import_environ); clean_env(import_env->argv); argv_free(import_env); if (chdir(var_queue_dir)) msg_fatal("chdir %s: %m", var_queue_dir); if (msg_verbose) msg_info("chdir %s", var_queue_dir); /* * Set up signal handlers and a runtime error handler so that we can * clean up incomplete output. * * postdrop_sig() uses the in-kernel SIGINT handler address as an atomic * variable to prevent nested postdrop_sig() calls. For this reason, the * SIGINT handler must be configured before other signal handlers are * allowed to invoke postdrop_sig(). */ signal(SIGPIPE, SIG_IGN); signal(SIGXFSZ, SIG_IGN); signal(SIGINT, postdrop_sig); signal(SIGQUIT, postdrop_sig); if (signal(SIGTERM, SIG_IGN) == SIG_DFL) signal(SIGTERM, postdrop_sig); if (signal(SIGHUP, SIG_IGN) == SIG_DFL) signal(SIGHUP, postdrop_sig); msg_cleanup(postdrop_cleanup); /* End of initializations. */ /* * Don't trust the caller's time information. */ GETTIMEOFDAY(&start); /* * Create queue file. mail_stream_file() never fails. Send the queue ID * to the caller. Stash away a copy of the queue file name so we can * clean up in case of a fatal error or an interrupt. */ dst = mail_stream_file(MAIL_QUEUE_MAILDROP, MAIL_CLASS_PUBLIC, var_pickup_service, 0444); attr_print(VSTREAM_OUT, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_QUEUEID, dst->id), ATTR_TYPE_END); vstream_fflush(VSTREAM_OUT); postdrop_path = mystrdup(VSTREAM_PATH(dst->stream)); /* * Copy stdin to file. The format is checked so that we can recognize * incomplete input and cancel the operation. With the sanity checks * applied here, the pickup daemon could skip format checks and pass a * file descriptor to the cleanup daemon. These are by no means all * sanity checks - the cleanup service and queue manager services will * reject messages that lack required information. * * If something goes wrong, slurp up the input before responding to the * client, otherwise the client will give up after detecting SIGPIPE. * * Allow attribute records if the attribute specifies the MIME body type * (sendmail -B). */ vstream_control(VSTREAM_IN, CA_VSTREAM_CTL_PATH("stdin"), CA_VSTREAM_CTL_END); buf = vstring_alloc(100); expected = segment_info; /* Override time information from the untrusted caller. */ rec_fprintf(dst->stream, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT, REC_TYPE_TIME_ARG(start)); for (;;) { /* Don't allow PTR records. */ rec_type = rec_get_raw(VSTREAM_IN, buf, var_line_limit, REC_FLAG_NONE); if (rec_type == REC_TYPE_EOF) { /* request cancelled */ mail_stream_cleanup(dst); if (remove(postdrop_path)) msg_warn("uid=%ld: remove %s: %m", (long) uid, postdrop_path); else if (msg_verbose) msg_info("remove %s", postdrop_path); myfree(postdrop_path); postdrop_path = 0; exit(0); } if (rec_type == REC_TYPE_ERROR) msg_fatal("uid=%ld: malformed input", (long) uid); if (strchr(*expected, rec_type) == 0) msg_fatal("uid=%ld: unexpected record type: %d", (long) uid, rec_type); if (rec_type == **expected) expected++; /* Override time information from the untrusted caller. */ if (rec_type == REC_TYPE_TIME) continue; /* Check these at submission time instead of pickup time. */ if (rec_type == REC_TYPE_FROM) from_count++; if (rec_type == REC_TYPE_RCPT) rcpt_count++; /* Limit the attribute types that users may specify. */ if (rec_type == REC_TYPE_ATTR) { if ((error_text = split_nameval(vstring_str(buf), &attr_name, &attr_value)) != 0) { msg_warn("uid=%ld: ignoring malformed record: %s: %.200s", (long) uid, error_text, vstring_str(buf)); continue; } #define STREQ(x,y) (strcmp(x,y) == 0) if ((STREQ(attr_name, MAIL_ATTR_ENCODING) && (STREQ(attr_value, MAIL_ATTR_ENC_7BIT) || STREQ(attr_value, MAIL_ATTR_ENC_8BIT) || STREQ(attr_value, MAIL_ATTR_ENC_NONE))) || STREQ(attr_name, MAIL_ATTR_DSN_ENVID) || STREQ(attr_name, MAIL_ATTR_DSN_NOTIFY) || rec_attr_map(attr_name) || (STREQ(attr_name, MAIL_ATTR_RWR_CONTEXT) && (STREQ(attr_value, MAIL_ATTR_RWR_LOCAL) || STREQ(attr_value, MAIL_ATTR_RWR_REMOTE))) || STREQ(attr_name, MAIL_ATTR_TRACE_FLAGS)) { /* XXX */ rec_fprintf(dst->stream, REC_TYPE_ATTR, "%s=%s", attr_name, attr_value); } else { msg_warn("uid=%ld: ignoring attribute record: %.200s=%.200s", (long) uid, attr_name, attr_value); } continue; } if (REC_PUT_BUF(dst->stream, rec_type, buf) < 0) { /* rec_get() errors must not clobber errno. */ saved_errno = errno; while ((rec_type = rec_get_raw(VSTREAM_IN, buf, var_line_limit, REC_FLAG_NONE)) != REC_TYPE_END && rec_type != REC_TYPE_EOF) if (rec_type == REC_TYPE_ERROR) msg_fatal("uid=%ld: malformed input", (long) uid); validate_input = 0; errno = saved_errno; break; } if (rec_type == REC_TYPE_END) break; } vstring_free(buf); /* * As of Postfix 2.7 the pickup daemon discards mail without recipients. * Such mail may enter the maildrop queue when "postsuper -r" is invoked * before the queue manager deletes an already delivered message. Looking * at file ownership is not a good way to make decisions on what mail to * discard. Instead, the pickup server now requires that new submissions * always have at least one recipient record. * * The Postfix sendmail command already rejects mail without recipients. * However, in the future postdrop may receive mail via other programs, * so we add a redundant recipient check here for future proofing. * * The test for the sender address is just for consistency of error * reporting (report at submission time instead of pickup time). Besides * the segment terminator records, there aren't any other mandatory * records in a Postfix submission queue file. */ if (validate_input && (from_count == 0 || rcpt_count == 0)) { status = CLEANUP_STAT_BAD; mail_stream_cleanup(dst); } /* * Finish the file. */ else if ((status = mail_stream_finish(dst, (VSTRING *) 0)) != 0) { msg_warn("uid=%ld: %m", (long) uid); postdrop_cleanup(); } /* * Disable deletion on fatal error before reporting success, so the file * will not be deleted after we have taken responsibility for delivery. */ if (postdrop_path) { junk = postdrop_path; postdrop_path = 0; myfree(junk); } /* * Send the completion status to the caller and terminate. */ attr_print(VSTREAM_OUT, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_STATUS, status), SEND_ATTR_STR(MAIL_ATTR_WHY, ""), ATTR_TYPE_END); vstream_fflush(VSTREAM_OUT); exit(status); }
static void perf_get_info(FB_API_HANDLE* handle, P* perf) { /************************************** * * P E R F _ g e t _ i n f o * ************************************** * * Functional description * Acquire timing and performance information. Some info comes * from the system and some from the database. * **************************************/ SSHORT buffer_length, item_length; ISC_STATUS_ARRAY jrd_status; #ifdef HAVE_GETTIMEOFDAY struct timeval tp; #else struct timeb time_buffer; #define LARGE_NUMBER 696600000 // to avoid overflow, get rid of decades) #endif // If there isn't a database, zero everything out if (!*handle) { memset(perf, 0, sizeof(PERF)); } // Get system time times(&perf->perf_times); #ifdef HAVE_GETTIMEOFDAY GETTIMEOFDAY(&tp); perf->perf_elapsed = tp.tv_sec * 100 + tp.tv_usec / 10000; #else ftime(&time_buffer); perf->perf_elapsed = (time_buffer.time - LARGE_NUMBER) * 100 + (time_buffer.millitm / 10); #endif if (!*handle) return; SCHAR buffer[256]; buffer_length = sizeof(buffer); item_length = sizeof(items); isc_database_info(jrd_status, handle, item_length, items, buffer_length, buffer); const char* p = buffer; while (true) switch (*p++) { case isc_info_reads: perf->perf_reads = get_parameter(&p); break; case isc_info_writes: perf->perf_writes = get_parameter(&p); break; case isc_info_marks: perf->perf_marks = get_parameter(&p); break; case isc_info_fetches: perf->perf_fetches = get_parameter(&p); break; case isc_info_num_buffers: perf->perf_buffers = get_parameter(&p); break; case isc_info_page_size: perf->perf_page_size = get_parameter(&p); break; case isc_info_current_memory: perf->perf_current_memory = get_parameter(&p); break; case isc_info_max_memory: perf->perf_max_memory = get_parameter(&p); break; case isc_info_end: return; case isc_info_error: switch (p[2]) { case isc_info_marks: perf->perf_marks = 0; break; case isc_info_current_memory: perf->perf_current_memory = 0; break; case isc_info_max_memory: perf->perf_max_memory = 0; break; } { const SLONG temp = isc_vax_integer(p, 2); fb_assert(temp <= MAX_SSHORT); p += temp + 2; } perf->perf_marks = 0; break; default: return; } }
/* Called by: covimp_test x3, zxid_call_epr, zxid_wsc_valid_resp */ int zxid_wsc_valid_re_env(zxid_conf* cf, zxid_ses* ses, const char* az_cred, struct zx_e_Envelope_s* env, const char* enve) { int n_refs = 0; struct zxsig_ref refs[ZXID_N_WSF_SIGNED_HEADERS]; struct timeval ourts; struct timeval srcts = {0,501000}; zxid_entity* wsc_meta; struct zx_wsse_Security_s* sec; struct zx_e_Header_s* hdr; struct zx_str* issuer; struct zx_str* logpath; struct zx_str* relto; struct zx_str ss; zxid_cgi cgi; GETTIMEOFDAY(&ourts, 0); zxid_set_fault(cf, ses, 0); zxid_set_tas3_status(cf, ses, 0); if (cf->valid_opt & ZXID_VALID_OPT_SKIP_RESP_HDR) { ERR("WARNING! Important response security validations disabled by VALID_OPT=0x%x", cf->valid_opt); return 1; } if (!env) { ERR("No <e:Envelope> found. enve(%s)", STRNULLCHK(enve)); zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No SOAP Envelope found.", "IDStarMsgNotUnderstood", 0, 0, 0)); return 0; } hdr = env->Header; if (!hdr) { ERR("No <e:Header> found. %d", 0); zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No SOAP Header found.", "IDStarMsgNotUnderstood", 0, 0, 0)); return 0; } if (!ZX_SIMPLE_ELEM_CHK(hdr->MessageID)) { ERR("No <a:MessageID> found. %d", 0); zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No MessageID header found.", "IDStarMsgNotUnderstood", 0, 0, 0)); return 0; } relto = ZX_GET_CONTENT(hdr->RelatesTo); if (relto && relto->len) { if (ses->wsc_msgid) { if (strlen(ses->wsc_msgid) == relto->len && !memcmp(ses->wsc_msgid, relto->s, relto->len)) { D("RelatesTo check OK %d",1); } else { /* N.B. [SOAPBinding2] p.27, ll.818-822 indicates RelatesTo checking as SHOULD. */ if (cf->relto_fatal) { ERR("<a:RelatesTo> (%.*s) does not match request msgid(%s).", relto->len, relto->s, ses->wsc_msgid); zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "RelatesTo in response does not match request MessageID.", "InvalidRefToMsgID", 0, 0, 0)); return 0; } else { INFO("<a:RelatesTo> (%.*s) does not match request msgid(%s), but configured to ignore this error (RELTO_FATAL=0).", relto->len, relto->s, ses->wsc_msgid); } } } else { INFO("Session does not have wsc_msgid. Skipping <a:RelatesTo> check. %d",0); } } else { if (cf->relto_fatal) { ERR("No <a:RelatesTo> found. %d", 0); zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No RelatesTo header found in reply.", "IDStarMsgNotUnderstood", 0, 0, 0)); return 0; } else { INFO("No <a:RelatesTo> found, but configured to ignore this (RELTO_FATAL=0). %d", 0); D("No RelTo OK enve(%s)", STRNULLCHK(enve)); } } if (!hdr->Sender || !hdr->Sender->providerID && !hdr->Sender->affiliationID) { ERR("No <b:Sender> found (or missing providerID or affiliationID). %p", hdr->Sender); zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No b:Sender header found (or missing providerID or affiliationID).", "IDStarMsgNotUnderstood", 0, 0, 0)); return 0; } issuer = &hdr->Sender->providerID->g; /* Validate message signature (*** add Issuer trusted check, CA validation, etc.) */ if (!(sec = hdr->Security)) { ERR("No <wsse:Security> found. %d", 0); zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No wsse:Security header found.", "IDStarMsgNotUnderstood", 0, 0, 0)); return 0; } wsc_meta = zxid_get_ent_ss(cf, issuer); if (!wsc_meta) { ses->sigres = ZXSIG_NO_SIG; if (cf->nosig_fatal) { INFO("Unable to find SAML metadata for Sender(%.*s), but configured to ignore this problem (NOSIG_FATAL=0).", issuer->len, issuer->s); } else { ERR("Unable to find SAML metadata for Sender(%.*s).", issuer->len, issuer->s); zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No unable to find SAML metadata for sender.", "ProviderIDNotValid", 0, 0, 0)); return 0; } } if (!sec->Signature || !sec->Signature->SignedInfo || !sec->Signature->SignedInfo->Reference) { ses->sigres = ZXSIG_NO_SIG; if (cf->wsp_nosig_fatal) { ERR("No Security/Signature found. %p", sec->Signature); zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "No wsse:Security/ds:Signature found.", TAS3_STATUS_NOSIG, 0, 0, 0)); return 0; } else { INFO("No Security/Signature found, but configured to ignore this problem (WSP_NOSIG_FATAL=0). %p", sec->Signature); } } else { ZERO(refs, sizeof(refs)); n_refs = zxid_hunt_sig_parts(cf, n_refs, refs, sec->Signature->SignedInfo->Reference, hdr, env->Body); /* *** Consider adding BDY and STR */ ses->sigres = zxsig_validate(cf->ctx, wsc_meta?wsc_meta->sign_cert:0, sec->Signature, n_refs, refs); zxid_sigres_map(ses->sigres, &cgi.sigval, &cgi.sigmsg); if (cf->sig_fatal && ses->sigres) { ERR("Fail due to failed message signature sigres=%d", ses->sigres); zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "Message signature did not validate.", TAS3_STATUS_BADSIG, 0, 0, 0)); return 0; } } if (!zxid_timestamp_chk(cf, ses, sec->Timestamp, &ourts, &srcts, TAS3_PEP_RS_IN, "e:Server")) return 0; if (hdr->UsageDirective) { if (hdr->UsageDirective->Obligation && ZX_GET_CONTENT(hdr->UsageDirective->Obligation->AttributeAssignment)) { ses->rcvd_usagedir = zx_str_to_c(cf->ctx, ZX_GET_CONTENT(hdr->UsageDirective->Obligation->AttributeAssignment)); D("Found TAS3 UsageDirective with obligation(%s)", ses->rcvd_usagedir); } else if (ZX_GET_CONTENT(hdr->UsageDirective)) { ses->rcvd_usagedir = zx_str_to_c(cf->ctx, ZX_GET_CONTENT(hdr->UsageDirective)); D("Found unknown UsageDirective(%s)", ses->rcvd_usagedir); } else { ERR("UsageDirective empty or not understood. %p", hdr->UsageDirective->Dict); } } zxid_ses_to_pool(cf, ses); zxid_snarf_eprs_from_ses(cf, ses); /* Harvest attributes and bootstrap(s) */ if (hdr->Status && hdr->Status->code && (hdr->Status->code->g.len != 2 || hdr->Status->code->g.s[0] != 'O' || hdr->Status->code->g.s[1] != 'K')) { ERR("TAS3 or app level error code(%.*s)", hdr->Status->code->g.len, hdr->Status->code->g.s); return 0; } /* Call Rs-In PDP */ if (!zxid_query_ctlpt_pdp(cf, ses, az_cred, env, TAS3_PEP_RS_IN, "e:Client", cf->pepmap_rsin)) { return 0; } /* *** execute (or store for future execution) the obligations. */ ss.s = (char*)enve; ss.len = strlen(enve); logpath = zxlog_path(cf, issuer, ZX_GET_CONTENT(hdr->MessageID), ZXLOG_RELY_DIR, ZXLOG_MSG_KIND, 1); if (zxlog_dup_check(cf, logpath, "validate response")) { if (cf->dup_msg_fatal) { zxlog_blob(cf, cf->log_rely_msg, logpath, &ss, "validate response dup err"); zxid_set_fault(cf, ses, zxid_mk_fault(cf, 0, TAS3_PEP_RS_IN, "e:Server", "Duplicate Message.", "DuplicateMsg", 0, 0, 0)); return 0; } else { INFO("Duplicate message detected, but configured to ignore this (DUP_MSG_FATAL=0). %d",0); } } zxlog_blob(cf, cf->log_rely_msg, logpath, &ss, "validate response"); zxlog(cf, &ourts, &srcts, 0, issuer, 0, ses->a7n?&ses->a7n->ID->g:0, ZX_GET_CONTENT(ses->nameid), "N", "K", "VALID", logpath->s, 0); return 1; }
/* * get_systime - return system time in NTP timestamp format. */ void get_systime( l_fp *now /* system time */ ) { double dtemp; #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK) struct timespec ts; /* seconds and nanoseconds */ /* * Convert Unix timespec from seconds and nanoseconds to NTP * seconds and fraction. */ # ifdef HAVE_CLOCK_GETTIME clock_gettime(CLOCK_REALTIME, &ts); # else getclock(TIMEOFDAY, &ts); # endif now->l_i = (int32)ts.tv_sec + JAN_1970; dtemp = 0; if (sys_tick > FUZZ) dtemp = ntp_random() * 2. / FRAC * sys_tick * 1e9; else if (sys_tick > 0) dtemp = ntp_random() * 2. / FRAC; dtemp = (ts.tv_nsec + dtemp) * 1e-9 + sys_residual; if (dtemp >= 1.) { dtemp -= 1.; now->l_i++; } else if (dtemp < 0) { dtemp += 1.; now->l_i--; } now->l_uf = (u_int32)(dtemp * FRAC); #else /* HAVE_CLOCK_GETTIME || HAVE_GETCLOCK */ struct timeval tv; /* seconds and microseconds */ /* * Convert Unix timeval from seconds and microseconds to NTP * seconds and fraction. */ GETTIMEOFDAY(&tv, NULL); now->l_i = tv.tv_sec + JAN_1970; dtemp = 0; if (sys_tick > FUZZ) dtemp = ntp_random() * 2. / FRAC * sys_tick * 1e6; else if (sys_tick > 0) dtemp = ntp_random() * 2. / FRAC; dtemp = (tv.tv_usec + dtemp) * 1e-6 + sys_residual; if (dtemp >= 1.) { dtemp -= 1.; now->l_i++; } else if (dtemp < 0) { dtemp += 1.; now->l_i--; } now->l_uf = (u_int32)(dtemp * FRAC); #endif /* HAVE_CLOCK_GETTIME || HAVE_GETCLOCK */ }
int handle_pkt ( int rpktl, struct pkt *rpkt, struct addrinfo *host ) { struct timeval tv_dst; int sw_case, digits; char *hostname = NULL, *ref, *ts_str = NULL; double offset, precision, root_dispersion; char addr_buf[INET6_ADDRSTRLEN]; char *p_SNTP_PRETEND_TIME; time_t pretend_time; if(rpktl > 0) sw_case = 1; else sw_case = rpktl; switch(sw_case) { case SERVER_UNUSEABLE: return -1; break; case PACKET_UNUSEABLE: break; case SERVER_AUTH_FAIL: break; case KOD_DEMOBILIZE: /* Received a DENY or RESTR KOD packet */ hostname = addrinfo_to_str(host); ref = (char *)&rpkt->refid; add_entry(hostname, ref); if (ENABLED_OPT(NORMALVERBOSE)) printf("sntp handle_pkt: Received KOD packet with code: %c%c%c%c from %s, demobilizing all connections\n", ref[0], ref[1], ref[2], ref[3], hostname); msyslog(LOG_WARNING, "Received a KOD packet with code %c%c%c%c from %s, demobilizing all connections", ref[0], ref[1], ref[2], ref[3], hostname); break; case KOD_RATE: /* Hmm... probably we should sleep a bit here */ break; case 1: if (ENABLED_OPT(NORMALVERBOSE)) { getnameinfo(host->ai_addr, host->ai_addrlen, addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST); printf("sntp handle_pkt: Received %i bytes from %s\n", rpktl, addr_buf); } GETTIMEOFDAY(&tv_dst, (struct timezone *)NULL); p_SNTP_PRETEND_TIME = getenv("SNTP_PRETEND_TIME"); if (p_SNTP_PRETEND_TIME) { #if SIZEOF_TIME_T == 4 sscanf(p_SNTP_PRETEND_TIME, "%ld", &pretend_time); #elif SIZEOF_TIME_T == 8 sscanf(p_SNTP_PRETEND_TIME, "%lld", &pretend_time); #else # include "GRONK: unexpected value for SIZEOF_TIME_T" #endif tv_dst.tv_sec = pretend_time; } offset_calculation(rpkt, rpktl, &tv_dst, &offset, &precision, &root_dispersion); for (digits = 0; (precision *= 10.) < 1.; ++digits) /* empty */ ; if (digits > 6) digits = 6; ts_str = tv_to_str(&tv_dst); printf("%s ", ts_str); if (offset > 0) printf("+"); printf("%.*f", digits, offset); if (root_dispersion > 0.) printf(" +/- %f secs", root_dispersion); printf("\n"); free(ts_str); if (p_SNTP_PRETEND_TIME) return 0; if (ENABLED_OPT(SETTOD) || ENABLED_OPT(ADJTIME)) return set_time(offset); return 0; } return 1; }
static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request) { const char *myname = "deliver_request_get"; const char *path; struct stat st; static VSTRING *queue_name; static VSTRING *queue_id; static VSTRING *nexthop; static VSTRING *encoding; static VSTRING *address; static VSTRING *client_name; static VSTRING *client_addr; static VSTRING *client_port; static VSTRING *client_proto; static VSTRING *client_helo; static VSTRING *sasl_method; static VSTRING *sasl_username; static VSTRING *sasl_sender; static VSTRING *rewrite_context; static VSTRING *dsn_envid; static RCPT_BUF *rcpt_buf; int rcpt_count; int dsn_ret; /* * Initialize. For some reason I wanted to allow for multiple instances * of a deliver_request structure, thus the hoopla with string * initialization and copying. */ if (queue_name == 0) { queue_name = vstring_alloc(10); queue_id = vstring_alloc(10); nexthop = vstring_alloc(10); encoding = vstring_alloc(10); address = vstring_alloc(10); client_name = vstring_alloc(10); client_addr = vstring_alloc(10); client_port = vstring_alloc(10); client_proto = vstring_alloc(10); client_helo = vstring_alloc(10); sasl_method = vstring_alloc(10); sasl_username = vstring_alloc(10); sasl_sender = vstring_alloc(10); rewrite_context = vstring_alloc(10); dsn_envid = vstring_alloc(10); rcpt_buf = rcpb_create(); } /* * Extract the queue file name, data offset, and sender address. Abort * the conversation when they send bad information. */ if (attr_scan(stream, ATTR_FLAG_STRICT, ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &request->flags, ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id, ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, &request->data_offset, ATTR_TYPE_LONG, MAIL_ATTR_SIZE, &request->data_size, ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, nexthop, ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding, ATTR_TYPE_STR, MAIL_ATTR_SENDER, address, ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid, ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, &dsn_ret, ATTR_TYPE_FUNC, msg_stats_scan, (void *) &request->msg_stats, /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */ ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_NAME, client_name, ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_ADDR, client_addr, ATTR_TYPE_STR, MAIL_ATTR_LOG_CLIENT_PORT, client_port, ATTR_TYPE_STR, MAIL_ATTR_LOG_PROTO_NAME, client_proto, ATTR_TYPE_STR, MAIL_ATTR_LOG_HELO_NAME, client_helo, /* XXX Should be encapsulated with ATTR_TYPE_FUNC. */ ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD, sasl_method, ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, sasl_username, ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, sasl_sender, /* XXX Ditto if we want to pass TLS certificate info. */ ATTR_TYPE_STR, MAIL_ATTR_RWR_CONTEXT, rewrite_context, ATTR_TYPE_INT, MAIL_ATTR_RCPT_COUNT, &rcpt_count, ATTR_TYPE_END) != 21) { msg_warn("%s: error receiving common attributes", myname); return (-1); } if (mail_open_ok(vstring_str(queue_name), vstring_str(queue_id), &st, &path) == 0) return (-1); /* Don't override hand-off time after deliver_pass() delegation. */ if (request->msg_stats.agent_handoff.tv_sec == 0) GETTIMEOFDAY(&request->msg_stats.agent_handoff); request->queue_name = mystrdup(vstring_str(queue_name)); request->queue_id = mystrdup(vstring_str(queue_id)); request->nexthop = mystrdup(vstring_str(nexthop)); request->encoding = mystrdup(vstring_str(encoding)); request->sender = mystrdup(vstring_str(address)); request->client_name = mystrdup(vstring_str(client_name)); request->client_addr = mystrdup(vstring_str(client_addr)); request->client_port = mystrdup(vstring_str(client_port)); request->client_proto = mystrdup(vstring_str(client_proto)); request->client_helo = mystrdup(vstring_str(client_helo)); request->sasl_method = mystrdup(vstring_str(sasl_method)); request->sasl_username = mystrdup(vstring_str(sasl_username)); request->sasl_sender = mystrdup(vstring_str(sasl_sender)); request->rewrite_context = mystrdup(vstring_str(rewrite_context)); request->dsn_envid = mystrdup(vstring_str(dsn_envid)); request->dsn_ret = dsn_ret; /* * Extract the recipient offset and address list. Skip over any * attributes from the sender that we do not understand. */ while (rcpt_count-- > 0) { if (attr_scan(stream, ATTR_FLAG_STRICT, ATTR_TYPE_FUNC, rcpb_scan, (void *) rcpt_buf, ATTR_TYPE_END) != 1) { msg_warn("%s: error receiving recipient attributes", myname); return (-1); } recipient_list_add(&request->rcpt_list, rcpt_buf->offset, vstring_str(rcpt_buf->dsn_orcpt), rcpt_buf->dsn_notify, vstring_str(rcpt_buf->orig_addr), vstring_str(rcpt_buf->address)); } if (request->rcpt_list.len <= 0) { msg_warn("%s: no recipients in delivery request for destination %s", request->queue_id, request->nexthop); return (-1); } /* * Open the queue file and set a shared lock, in order to prevent * duplicate deliveries when the queue is flushed immediately after queue * manager restart. * * The queue manager locks the file exclusively when it enters the active * queue, and releases the lock before starting deliveries from that * file. The queue manager does not lock the file again when reading more * recipients into memory. When the queue manager is restarted, the new * process moves files from the active queue to the incoming queue to cool * off for a while. Delivery agents should therefore never try to open a * file that is locked by a queue manager process. * * Opening the queue file can fail for a variety of reasons, such as the * system running out of resources. Instead of throwing away mail, we're * raising a fatal error which forces the mail system to back off, and * retry later. */ #define DELIVER_LOCK_MODE (MYFLOCK_OP_SHARED | MYFLOCK_OP_NOWAIT) request->fp = mail_queue_open(request->queue_name, request->queue_id, O_RDWR, 0); if (request->fp == 0) { if (errno != ENOENT) msg_fatal("open %s %s: %m", request->queue_name, request->queue_id); msg_warn("open %s %s: %m", request->queue_name, request->queue_id); return (-1); } if (msg_verbose) msg_info("%s: file %s", myname, VSTREAM_PATH(request->fp)); if (myflock(vstream_fileno(request->fp), INTERNAL_LOCK, DELIVER_LOCK_MODE) < 0) msg_fatal("shared lock %s: %m", VSTREAM_PATH(request->fp)); close_on_exec(vstream_fileno(request->fp), CLOSE_ON_EXEC); return (0); }
static int smtp_bulk_fail(SMTP_STATE *state, int throttle_queue) { DELIVER_REQUEST *request = state->request; SMTP_SESSION *session = state->session; DSN_BUF *why = state->why; RECIPIENT *rcpt; int status; int aggregate_status; int soft_error = (STR(why->status)[0] == '4'); int soft_bounce_error = (STR(why->status)[0] == '5' && var_soft_bounce); int nrcpt; /* * Don't defer the recipients just yet when this error qualifies them for * delivery to a backup server. Just log something informative to show * why we're skipping this host. */ if ((soft_error || soft_bounce_error) && (state->misc_flags & SMTP_MISC_FLAG_FINAL_SERVER) == 0) { msg_info("%s: %s", request->queue_id, STR(why->reason)); for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) { rcpt = request->rcpt_list.info + nrcpt; if (SMTP_RCPT_ISMARKED(rcpt)) continue; SMTP_RCPT_KEEP(state, rcpt); } } /* * Defer or bounce all the remaining recipients, and delete them from the * delivery request. If a bounce fails, defer instead and do not qualify * the recipient for delivery to a backup server. */ else { /* * If we are still in the connection set-up phase, update the set-up * completion time here, otherwise the time spent in set-up latency * will be attributed as message transfer latency. * * All remaining recipients have failed at this point, so we update the * delivery completion time stamp so that multiple recipient status * records show the same delay values. */ if (request->msg_stats.conn_setup_done.tv_sec == 0) { GETTIMEOFDAY(&request->msg_stats.conn_setup_done); request->msg_stats.deliver_done = request->msg_stats.conn_setup_done; } else GETTIMEOFDAY(&request->msg_stats.deliver_done); (void) DSN_FROM_DSN_BUF(why); aggregate_status = 0; for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) { rcpt = request->rcpt_list.info + nrcpt; if (SMTP_RCPT_ISMARKED(rcpt)) continue; status = (soft_error ? defer_append : bounce_append) (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id, &request->msg_stats, rcpt, session ? session->namaddrport : "none", &why->dsn); if (status == 0) deliver_completed(state->src, rcpt->offset); SMTP_RCPT_DROP(state, rcpt); aggregate_status |= status; } state->status |= aggregate_status; if ((state->misc_flags & SMTP_MISC_FLAG_COMPLETE_SESSION) == 0 && throttle_queue && aggregate_status && request->hop_status == 0) request->hop_status = DSN_COPY(&why->dsn); } /* * Don't cache this session. We can't talk to this server. */ if (throttle_queue && session) DONT_CACHE_THROTTLED_SESSION; return (-1); }
static FORWARD_INFO *forward_open(DELIVER_REQUEST *request, const char *sender) { VSTRING *buffer = vstring_alloc(100); FORWARD_INFO *info; VSTREAM *cleanup; #define FORWARD_OPEN_RETURN(res) do { \ vstring_free(buffer); \ return (res); \ } while (0) /* * Contact the cleanup service and save the new mail queue id. Request * that the cleanup service bounces bad messages to the sender so that we * can avoid the trouble of bounce management. * * In case you wonder what kind of bounces, examples are "too many hops", * "message too large", perhaps some others. The reason not to bounce * ourselves is that we don't really know who the recipients are. */ cleanup = mail_connect(MAIL_CLASS_PUBLIC, var_cleanup_service, BLOCKING); if (cleanup == 0) { msg_warn("connect to %s/%s: %m", MAIL_CLASS_PUBLIC, var_cleanup_service); FORWARD_OPEN_RETURN(0); } close_on_exec(vstream_fileno(cleanup), CLOSE_ON_EXEC); if (attr_scan(cleanup, ATTR_FLAG_STRICT, RECV_ATTR_STR(MAIL_ATTR_QUEUEID, buffer), ATTR_TYPE_END) != 1) { vstream_fclose(cleanup); FORWARD_OPEN_RETURN(0); } info = (FORWARD_INFO *) mymalloc(sizeof(FORWARD_INFO)); info->cleanup = cleanup; info->queue_id = mystrdup(STR(buffer)); GETTIMEOFDAY(&info->posting_time); #define FORWARD_CLEANUP_FLAGS \ (CLEANUP_FLAG_BOUNCE | CLEANUP_FLAG_MASK_INTERNAL \ | smtputf8_autodetect(MAIL_SRC_MASK_FORWARD) \ | ((request->smtputf8 & SMTPUTF8_FLAG_REQUESTED) ? \ CLEANUP_FLAG_SMTPUTF8 : 0)) attr_print(cleanup, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_FLAGS, FORWARD_CLEANUP_FLAGS), ATTR_TYPE_END); /* * Send initial message envelope information. For bounces, set the * designated sender: mailing list owner, posting user, whatever. */ rec_fprintf(cleanup, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT, REC_TYPE_TIME_ARG(info->posting_time)); rec_fputs(cleanup, REC_TYPE_FROM, sender); /* * Don't send the original envelope ID or full/headers return mask if it * was reset due to mailing list expansion. */ if (request->dsn_ret) rec_fprintf(cleanup, REC_TYPE_ATTR, "%s=%d", MAIL_ATTR_DSN_RET, request->dsn_ret); if (request->dsn_envid && *(request->dsn_envid)) rec_fprintf(cleanup, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_DSN_ENVID, request->dsn_envid); /* * Zero-length attribute values are place holders for unavailable * attribute values. See qmgr_message.c. They are not meant to be * propagated to queue files. */ #define PASS_ATTR(fp, name, value) do { \ if ((value) && *(value)) \ rec_fprintf((fp), REC_TYPE_ATTR, "%s=%s", (name), (value)); \ } while (0) /* * XXX encapsulate these as one object. */ PASS_ATTR(cleanup, MAIL_ATTR_LOG_CLIENT_NAME, request->client_name); PASS_ATTR(cleanup, MAIL_ATTR_LOG_CLIENT_ADDR, request->client_addr); PASS_ATTR(cleanup, MAIL_ATTR_LOG_PROTO_NAME, request->client_proto); PASS_ATTR(cleanup, MAIL_ATTR_LOG_HELO_NAME, request->client_helo); PASS_ATTR(cleanup, MAIL_ATTR_SASL_METHOD, request->sasl_method); PASS_ATTR(cleanup, MAIL_ATTR_SASL_USERNAME, request->sasl_username); PASS_ATTR(cleanup, MAIL_ATTR_SASL_SENDER, request->sasl_sender); PASS_ATTR(cleanup, MAIL_ATTR_LOG_IDENT, request->log_ident); PASS_ATTR(cleanup, MAIL_ATTR_RWR_CONTEXT, request->rewrite_context); FORWARD_OPEN_RETURN(info); }
void ClientThread(void *dummy) { THREAD FILE *logfile; THREAD stats_t timestat; THREAD rqst_timer_t timerarray[MAXNUMOFFILES]; THREAD SOCKET mastersock = BADSOCKET_VALUE; /* connection to webmaster */ THREAD page_stats_t *page_stats; /* actually a dynamic array */ int loopcnt = 0; int filecnt; int loop; int ran_number; int page_index; int page_number; int file_count = 0; char file_name[50]; struct timeval runningtime; time_t junk; int i; int returnval; /* * INITIALIZE DATA */ page_stats = (page_stats_t *)mymalloc((number_of_pages)*sizeof(page_stats_t)); for (i=0; i < number_of_pages; i++) { page_stats_init(&(page_stats[i])); } if (debug) { /* * OPEN A DEBUG FILE */ fflush(stderr); sprintf(file_name, "%s.%d", DEBUG_FILE, (int)getpid()); debugfile = fopen(file_name, "w+"); if (debugfile == 0) errexit("Can't open debug file\n"); D_PRINTF( "Running in debug mode, %d\n",amclient ); } if (record_all_transactions) { /* * OPEN A LOG FILE. */ sprintf(file_name, "%s%d", LOG_FILE, (int)getpid()); returnerr("Log file is %s\n", file_name); logfile = fopen(file_name, "w+"); } /* Initialize random number generator */ junk = getpid (); rand_r(&junk); D_PRINTF( "Random seed: %d\n", junk ); for (i=0; i < MAXNUMOFFILES; i++) { rqtimer_init(&(timerarray[i])); } stats_init(×tat); D_PRINTF( "Number of files %d\n", numfiles ); timestat.total_num_of_files = numfiles; if (amclient) { /* * WE ARE A CLIENT PROCESS. (i.e. WE ARE NOT RUN BY A USER, BUT BY * THE MASTER WWWSTONE PROCESS. WE NEED TO CONNECT TO THE * MASTER WHO WILL SYNCHRONIZE ALL THE CLIENTS. */ D_PRINTF( "Trying to connect with %s\n",connectstr ); mastersock = connecttomaster(connectstr); D_PRINTF( "connecttomaster returns %d, %s\n", mastersock, neterrstr() ); if(BADSOCKET(mastersock)) { /* * ERROR CONNECTING TO THE MASTER. ABORT. */ errexit("Error connecting to the master: %s\n", neterrstr()); } } /* END IF CLIENT */ #ifdef WIN32 /* Tell parent we're ready */ InterlockedIncrement(&CounterSemaphore); /* Wait for main() thread to release us */ WaitForSingleObject(hSemaphore, INFINITE); ReleaseSemaphore(hSemaphore, 1, 0); #endif /* WIN32 */ if (testtime != 0) { /* * IF RUNNING A TIMED TEST, WE WILL LOOP * UNTIL THE ALARM GOES OFF. * WE'LL ALSO NEED TO SET THE SIGNAL HANDLER */ #ifndef WIN32 /*signal(SIGALRM, alarmhandler);*/ /* * SEND SIGALRM IN testtime SECONDS */ /*alarm(testtime);*/ #endif /* WIN32 */ } /* * AND THEY'RE OFF... */ if (testtime) numloops = INFINITY; GETTIMEOFDAY(&(timestat.starttime), &(timestat.starttimezone)); /* LOOP UNTIL WE HIT numloops, OR WE RUN OUT OF TIME */ for(loopcnt = 0; (loopcnt < numloops) && !timeexpired; loopcnt++) { /* * THIS IS WHERE LOAD TESTING IS DONE. * GET A RANDOM NUMBER, THEN INDEX INTO THE * PAGE, AND THEN REQUEST THAT SET OF FILES. */ if (uil_filelist_f) /* HAVE FILELIST */ { D_PRINTF( "Have filelist\n" ); /* if (testtime != 0) /* RUNNING IN TIMED MODE */ if (1) { D_PRINTF( "Running in timed mode\n" ); /* random number between 0 and totalweight-1 */ junk = getpid (); ran_number = (rand_r(&junk) % total_weight); D_PRINTF( "random %ld\n", ran_number ); /* loop through pages, find correct one * while ran_number is positive, decrement it * by the load_num of the current page * example: ran_number is 5, pages have weights of 10 and 10 * first iteration page_index = 0, ran_number = -5 * iteration halted, page_index = 0 */ page_index = -1; while (ran_number >= 0) { page_index++; D_PRINTF( "Current page index %d: %ld - %d\n", page_index, ran_number, load_file_list[page_index].load_num ); ran_number -= load_file_list[page_index].load_num; } if (page_index >= number_of_pages) { page_index--; } D_PRINTF( "Final page index %d\n", page_index ); filecnt = makeload(load_file_list[page_index].num_of_files, page_index, timerarray, ×tat, mastersock, page_stats); testtime = 1; } else /* NOT RUNNING IN TIMED MODE */ { for (page_number = 0; page_number < number_of_pages; page_number++) { filecnt = makeload(load_file_list[page_number].num_of_files, page_number, timerarray, ×tat, mastersock, page_stats); } /* END for page_number */ } /* END if/else TIMED MODE */ } else /* NO FILELIST */ { D_PRINTF( "No filelist\n" ); /* * LOOP THROUGH UNTIL numfiles TIMES OR UNTIL TIMER EXPIRES * AND ALARM SETS filecnt TO INFINITY. */ /* does this still work?? */ /* filecnt = makeload(numfiles, -1, timerarray); */ } /* END if HAVE FILELIST */ if (filecnt > 0) file_count += filecnt; } /* END while loopcnt */ GETTIMEOFDAY(&(timestat.endtime), &(timestat.endtimezone)); D_PRINTF( "Test run complete\n" ); signal(SIGALRM, 0); if (testtime == 0) { numfiles = loopcnt; if (uil_filelist_f) { numfiles = file_count; } } /* This option ( "-R" ) looks broken (e.g. l > 50) -- JEF 2/15/96 */ if (record_all_transactions) { /* * DUMP THE LOG FILE INFORMATION. */ for (loop=0; loop < (loopcnt * file_count); loop++) { fprintf(logfile, " entertime \t%d.%d\n" " beforeconnect \t%d.%d\n" " afterconnect \t%d.%d\n" " beforeheader \t%d.%d\n" " afterheader \t%d.%d\n" " afterbody \t%d.%d\n" " exittime \t%d.%d\n" " total bytes \t%d\n" " body bytes\t%d\n", timerarray[loop].entertime.tv_sec, timerarray[loop].entertime.tv_usec, timerarray[loop].beforeconnect.tv_sec, timerarray[loop].beforeconnect.tv_usec, timerarray[loop].afterconnect.tv_sec, timerarray[loop].afterconnect.tv_usec, timerarray[loop].beforeheader.tv_sec, timerarray[loop].beforeheader.tv_usec, timerarray[loop].afterheader.tv_sec, timerarray[loop].afterheader.tv_usec, timerarray[loop].afterbody.tv_sec, timerarray[loop].afterbody.tv_usec, timerarray[loop].exittime.tv_sec, timerarray[loop].exittime.tv_usec, timerarray[loop].totalbytes, timerarray[loop].bodybytes); } /* end for loop */ } /* end if recording all transactions */ D_PRINTF( "total errors: %d\n",timestat.rs.totalerrs ); /* gethostname(timestat.hostname,MAXHOSTNAMELEN); */ /* D_PRINTF( "Test for host: %s\n",timestat.hostname ); */ D_PRINTF( "Server is: %s running at port number: %d\n", webserver,portnum ); /* sprintf(timestat.hostname,"%s:%d",timestat.hostname,getpid()); */ if (amclient) /* CLIENT TO A WEBMASTER */ { char *stats_as_text; /* * SEND THE TIMING DATA TO THE MASTER */ stats_as_text = stats_to_text(×tat); D_PRINTF( "stats_to_text returned %s\n", stats_as_text ); returnval = senddata(mastersock, stats_as_text, SIZEOF_STATSTEXTBASE + number_of_pages*SIZEOF_DOUBLETEXT); D_PRINTF( "Wrote time stats to master %d\n", returnval ); if (returnval < 1) { D_PRINTF( "Error while writing time stats: %s\n", neterrstr() ); errexit("Error while writing time stats: %s\n", neterrstr()); } if (uil_filelist_f) /* write pagestats */ { char *page_stats_as_text; for (i = 0; i < number_of_pages; i++) { D_PRINTF( "On page_stats[%d]\n", i ); page_stats_as_text = page_stats_to_text(&page_stats[i]); returnval = strlen(page_stats_as_text); D_PRINTF( "page_stats_to_text[%d] returned %d\n", i, returnval ); returnval = senddata(mastersock, page_stats_as_text, SIZEOF_PAGESTATSTEXT); if (returnval < 1) { D_PRINTF( "Error while writing page_stats[%d]: %s\n", i, neterrstr() ); errexit("Error while writing page_stats[%d]: %s\n", i, neterrstr()); } /* end if */ D_PRINTF( "Wrote %d bytes of page_stats[%d] to master\n", returnval, i ); } /* end for */ } /* end if filelist */ D_PRINTF( "About to close socket\n" ); if (NETCLOSE(mastersock)) D_PRINTF( "Close socket error: %s\n", neterrstr() ); } else /* NOT A CLIENT TO A WEBMASTER */ { if (testtime) { printf("Test ran for: %d minutes\n",(testtime/60)); } else { printf("Test ran for: %d iterations.\n",numloops); } compdifftime(&(timestat.endtime), &(timestat.starttime), &(runningtime)); printf("Total time of test (sec) %d.%d\n", runningtime.tv_sec, runningtime.tv_usec); printf("Files retrieved per iteration: %d\n",numfiles); /* 'per iteration' */ printf("----------------------------------\n"); printf("Totals:\n\n"); rqstat_print(&(timestat.rs)); if (timestat.rs.totalconnects == 0) goto end; printf("Thruput = %5.2lf Kbytes/sec\n", thruputpersec(timestat.rs.totalbytes, &runningtime) / 1000); if (uil_filelist_f && numloops && verbose) { for (loop = 0; loop < number_of_pages; loop++) { if (timestat.page_numbers[loop] != 0) { printf ("===============================================================================\n"); printf ("Page # %d\n\n", loop); printf ("Total number of times page was hit %d\n", page_stats[loop].totalpages); rqstat_print(&(page_stats[loop].rs)); printf ("Page size %d \n", page_stats[loop].page_size); printf ("===============================================================================\n\n"); } /* END if timestat */ } /* END for loop */ } /* END if filelist */ } /* END if client */ end: if(record_all_transactions) fclose(logfile); if(debug) { D_PRINTF( "Client exiting.\n" ); fclose(debugfile); } #ifdef WIN32 /* tell parent we're done */ InterlockedIncrement(&CounterSemaphore); #endif /* WIN32 */ } /* END ClientThread() */
/* * step_systime - step the system clock. */ int step_systime( double now ) { struct timeval timetv, adjtv, oldtimetv; int isneg = 0; double dtemp; #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK) struct timespec ts; #endif dtemp = sys_residual + now; if (dtemp < 0) { isneg = 1; dtemp = - dtemp; adjtv.tv_sec = (int32)dtemp; adjtv.tv_usec = (u_int32)((dtemp - (double)adjtv.tv_sec) * 1e6 + .5); } else { adjtv.tv_sec = (int32)dtemp; adjtv.tv_usec = (u_int32)((dtemp - (double)adjtv.tv_sec) * 1e6 + .5); } #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK) #ifdef HAVE_CLOCK_GETTIME (void) clock_gettime(CLOCK_REALTIME, &ts); #else (void) getclock(TIMEOFDAY, &ts); #endif timetv.tv_sec = ts.tv_sec; timetv.tv_usec = ts.tv_nsec / 1000; #else /* not HAVE_GETCLOCK */ (void) GETTIMEOFDAY(&timetv, (struct timezone *)0); #endif /* not HAVE_GETCLOCK */ oldtimetv = timetv; #ifdef DEBUG if (debug) printf("step_systime: step %.6f residual %.6f\n", now, sys_residual); #endif if (isneg) { timetv.tv_sec -= adjtv.tv_sec; timetv.tv_usec -= adjtv.tv_usec; if (timetv.tv_usec < 0) { timetv.tv_sec--; timetv.tv_usec += 1000000; } } else { timetv.tv_sec += adjtv.tv_sec; timetv.tv_usec += adjtv.tv_usec; if (timetv.tv_usec >= 1000000) { timetv.tv_sec++; timetv.tv_usec -= 1000000; } } if (ntp_set_tod(&timetv, (struct timezone *)0) != 0) { msyslog(LOG_ERR, "Can't set time of day: %m"); return (0); } sys_residual = 0; #ifdef NEED_HPUX_ADJTIME /* * CHECKME: is this correct when called by ntpdate????? */ _clear_adjtime(); #endif /* * FreeBSD, for example, has: * struct utmp { * char ut_line[UT_LINESIZE]; * char ut_name[UT_NAMESIZE]; * char ut_host[UT_HOSTSIZE]; * long ut_time; * }; * and appends line="|", name="date", host="", time for the OLD * and appends line="{", name="date", host="", time for the NEW * to _PATH_WTMP . * * Some OSes have utmp, some have utmpx. */ /* * Write old and new time entries in utmp and wtmp if step adjustment * is greater than one second. * * This might become even Uglier... */ if (oldtimetv.tv_sec != timetv.tv_sec) { #ifdef HAVE_UTMP_H struct utmp ut; #endif #ifdef HAVE_UTMPX_H struct utmpx utx; #endif #ifdef HAVE_UTMP_H memset((char *)&ut, 0, sizeof(ut)); #endif #ifdef HAVE_UTMPX_H memset((char *)&utx, 0, sizeof(utx)); #endif /* UTMP */ #ifdef UPDATE_UTMP # ifdef HAVE_PUTUTLINE ut.ut_type = OLD_TIME; (void)strcpy(ut.ut_line, OTIME_MSG); ut.ut_time = oldtimetv.tv_sec; pututline(&ut); setutent(); ut.ut_type = NEW_TIME; (void)strcpy(ut.ut_line, NTIME_MSG); ut.ut_time = timetv.tv_sec; pututline(&ut); endutent(); # else /* not HAVE_PUTUTLINE */ # endif /* not HAVE_PUTUTLINE */ #endif /* UPDATE_UTMP */ /* UTMPX */ #ifdef UPDATE_UTMPX # ifdef HAVE_PUTUTXLINE utx.ut_type = OLD_TIME; (void)strcpy(utx.ut_line, OTIME_MSG); utx.ut_tv = oldtimetv; pututxline(&utx); setutxent(); utx.ut_type = NEW_TIME; (void)strcpy(utx.ut_line, NTIME_MSG); utx.ut_tv = timetv; pututxline(&utx); endutxent(); # else /* not HAVE_PUTUTXLINE */ # endif /* not HAVE_PUTUTXLINE */ #endif /* UPDATE_UTMPX */ /* WTMP */ #ifdef UPDATE_WTMP # ifdef HAVE_PUTUTLINE utmpname(WTMP_FILE); ut.ut_type = OLD_TIME; (void)strcpy(ut.ut_line, OTIME_MSG); ut.ut_time = oldtimetv.tv_sec; pututline(&ut); ut.ut_type = NEW_TIME; (void)strcpy(ut.ut_line, NTIME_MSG); ut.ut_time = timetv.tv_sec; pututline(&ut); endutent(); # else /* not HAVE_PUTUTLINE */ # endif /* not HAVE_PUTUTLINE */ #endif /* UPDATE_WTMP */ /* WTMPX */ #ifdef UPDATE_WTMPX # ifdef HAVE_PUTUTXLINE utx.ut_type = OLD_TIME; utx.ut_tv = oldtimetv; (void)strcpy(utx.ut_line, OTIME_MSG); # ifdef HAVE_UPDWTMPX updwtmpx(WTMPX_FILE, &utx); # else /* not HAVE_UPDWTMPX */ # endif /* not HAVE_UPDWTMPX */ # else /* not HAVE_PUTUTXLINE */ # endif /* not HAVE_PUTUTXLINE */ # ifdef HAVE_PUTUTXLINE utx.ut_type = NEW_TIME; utx.ut_tv = timetv; (void)strcpy(utx.ut_line, NTIME_MSG); # ifdef HAVE_UPDWTMPX updwtmpx(WTMPX_FILE, &utx); # else /* not HAVE_UPDWTMPX */ # endif /* not HAVE_UPDWTMPX */ # else /* not HAVE_PUTUTXLINE */ # endif /* not HAVE_PUTUTXLINE */ #endif /* UPDATE_WTMPX */ } return (1); }
int zxid_sp_sso_finalize_jwt(zxid_conf* cf, zxid_cgi* cgi, zxid_ses* ses, const char* jwt) { char* err = "S"; /* See: RES in zxid-log.pd, section "ZXID Log Format" */ struct zx_str ss; struct timeval ourts; struct timeval srcts = {0,501000}; struct zx_str* logpath; char* p; char* claims; ses->jwt = (char*)jwt; //ses->rs = ; ses->ssores = 1; GETTIMEOFDAY(&ourts, 0); D_INDENT("ssof: "); claims = zxid_decode_jwt(cf, cgi, ses, jwt); if (!claims) { ERR("JWT decode error jwt(%s)", STRNULLCHKD(jwt)); goto erro; } //ses->nid = zx_json_extract_dup(cf->ctx, claims, "\"sub\""); ses->nid = zx_json_extract_dup(cf->ctx, claims, "\"user_id\""); if (!ses->nid) { ERR("JWT decode: no user_id found in jwt(%s)", STRNULLCHKD(jwt)); goto erro; } ses->nidfmt = 1; /* Assume federation */ ses->tgtjwt = ses->jwt; ses->tgt = ses->nid; ses->tgtfmt = 1; /* Assume federation */ p = zx_json_extract_dup(cf->ctx, claims, "\"iss\""); ses->issuer = zx_ref_str(cf->ctx, p); if (!p) { ERR("JWT decode: no iss found in jwt(%s)", STRNULLCHKD(jwt)); goto erro; } D("SSOJWT received. NID(%s) FMT(%d)", ses->nid, ses->nidfmt); // *** should some signature validation happen here, using issuer (idp) meta? cgi->sigval = "N"; cgi->sigmsg = "Assertion was not signed."; ses->sigres = ZXSIG_NO_SIG; if (cf->log_rely_a7n) { DD("Logging rely... %d", 0); ss.s = (char*)jwt; ss.len = strlen(jwt); logpath = zxlog_path(cf, ses->issuer, &ss, ZXLOG_RELY_DIR, ZXLOG_JWT_KIND, 1); if (logpath) { ses->sso_a7n_path = ses->tgt_a7n_path = zx_str_to_c(cf->ctx, logpath); if (zxlog_dup_check(cf, logpath, "SSO JWT")) { if (cf->dup_a7n_fatal) { err = "C"; zxlog_blob(cf, cf->log_rely_a7n, logpath, &ss, "sp_sso_finalize_jwt dup err"); goto erro; } } zxlog_blob(cf, cf->log_rely_a7n, logpath, &ss, "sp_sso_finalize_jwt"); } } DD("Creating session... %d", 0); ses->ssores = 0; zxid_put_ses(cf, ses); //*** zxid_snarf_eprs_from_ses(cf, ses); /* Harvest attributes and bootstrap(s) */ cgi->msg = "SSO completed and session created."; cgi->op = '-'; /* Make sure management screen does not try to redispatch. */ zxid_put_user(cf, 0, 0, 0, zx_ref_str(cf->ctx, ses->nid), 0); DD("Logging... %d", 0); ss.s = ses->nid; ss.len = strlen(ss.s); zxlog(cf, &ourts, &srcts, 0, ses->issuer, 0, 0, &ss, cgi->sigval, "K", "NEWSESJWT", ses->sid, "sesix(%s)", STRNULLCHKD(ses->sesix)); zxlog(cf, &ourts, &srcts, 0, ses->issuer, 0, 0, &ss, cgi->sigval, "K", ses->nidfmt?"FEDSSOJWT":"TMPSSOJWT", STRNULLCHKD(ses->sesix), 0); #if 0 if (cf->idp_ena) { /* (PXY) Middle IdP of Proxy IdP flow */ if (cgi->rs && cgi->rs[0]) { D("ProxyIdP got RelayState(%s) ar(%s)", cgi->rs, STRNULLCHK(cgi->ssoreq)); cgi->saml_resp = 0; /* Clear Response to prevent re-interpretation. We want Request. */ cgi->ssoreq = cgi->rs; zxid_decode_ssoreq(cf, cgi); cgi->op = 'V'; D_DEDENT("ssof: "); return ZXID_IDP_REQ; /* Cause zxid_simple_idp_an_ok_do_rest() to be called from zxid_sp_dispatch(); */ } else { INFO("Middle IdP of Proxy IdP flow did not receive RelayState from upstream IdP %p", cgi->rs); } } #endif D_DEDENT("ssof: "); return ZXID_SSO_OK; erro: ERR("SSOJWT fail (%s)", err); cgi->msg = "SSO failed. This could be due to signature, timeout, etc., technical failures, or by policy."; zxlog(cf, &ourts, &srcts, 0, ses->issuer, 0, 0, 0, cgi->sigval, err, ses->nidfmt?"FEDSSOJWT":"TMPSSOJWT", STRNULLCHKD(ses->sesix), "Error."); D_DEDENT("ssof: "); return 0; }
static void waitForSomething(void) { #if defined(__vms) && ( __VMS_VER < 70000000 ) static struct timeval zerotime = { 0 }; unsigned int timer_efn; #define timer_id 'glut' /* random :-) number */ unsigned int wait_mask; #else static struct timeval zerotime = { 0, 0 }; #if !defined(_WIN32) fd_set fds; #endif #endif struct timeval now, timeout, waittime; #if !defined(_WIN32) int rc; #endif /* Flush X protocol since XPending does not do this implicitly. */ XFlush(__glutDisplay); if (XPending(__glutDisplay)) { /* It is possible (but quite rare) that XFlush may have needed to wait for a writable X connection file descriptor, and in the process, may have had to read off X protocol from the file descriptor. If XPending is true, this case occured and we should avoid waiting in select since X protocol buffered within Xlib is due to be processed and potentially no more X protocol is on the file descriptor, so we would risk waiting improperly in select. */ goto immediatelyHandleXinput; } #if defined(__vms) && ( __VMS_VER < 70000000 ) timeout = __glutTimerList->timeout; GETTIMEOFDAY(&now); wait_mask = 1 << (__glutConnectionFD & 31); if (IS_AFTER(now, timeout)) { /* We need an event flag for the timer. */ /* XXX The `right' way to do this is to use LIB$GET_EF, but since it needs to be in the same cluster as the EFN for the display, we will have hack it. */ timer_efn = __glutConnectionFD - 1; if ((timer_efn / 32) != (__glutConnectionFD / 32)) { timer_efn = __glutConnectionFD + 1; } rc = SYS$CLREF(timer_efn); rc = SYS$SETIMR(timer_efn, &timeout, NULL, timer_id, 0); wait_mask |= 1 << (timer_efn & 31); } else { timer_efn = 0; } rc = SYS$WFLOR(__glutConnectionFD, wait_mask); if (timer_efn != 0 && SYS$CLREF(timer_efn) == SS$_WASCLR) { rc = SYS$CANTIM(timer_id, PSL$C_USER); } /* XXX There does not seem to be checking of "rc" in the code above. Can any of the SYS$ routines above fail? */ #else /* not vms6.2 or lower */ #if !defined(_WIN32) FD_ZERO(&fds); FD_SET(__glutConnectionFD, &fds); #endif timeout = __glutTimerList->timeout; GETTIMEOFDAY(&now); if (IS_AFTER(now, timeout)) { TIMEDELTA(waittime, timeout, now); } else { waittime = zerotime; } #if !defined(_WIN32) rc = select(__glutConnectionFD + 1, &fds, NULL, NULL, &waittime); if (rc < 0 && errno != EINTR) __glutFatalError("select error."); #else #if 0 /* XXX Nate, what is this junk? */ /* Set up a timer to fire in at least a millisecond, then wait for the message. This should act like a select. */ SetTimer(NULL, 2, waittime.tv_usec, NULL); WaitMessage(); KillTimer(NULL, 2); #endif /* Actually, a sleep seems to do the trick -- do we even need this? */ Sleep(0); #endif #endif /* not vms6.2 or lower */ /* Without considering the cause of select unblocking, check for pending X events and handle any timeouts (by calling processEventsAndTimeouts). We always look for X events even if select returned with 0 (indicating a timeout); otherwise we risk starving X event processing by continous timeouts. */ if (XPending(__glutDisplay)) { immediatelyHandleXinput: processEventsAndTimeouts(); } else { if (__glutTimerList) handleTimeouts(); } }
int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr) { const char *myname = "deliver_maildir"; char *newdir; char *tmpdir; char *curdir; char *tmpfile; char *newfile; DSN_BUF *why = state.msg_attr.why; VSTRING *buf; VSTREAM *dst; int mail_copy_status; int deliver_status; int copy_flags; struct stat st; struct timeval starttime; GETTIMEOFDAY(&starttime); /* * Make verbose logging easier to understand. */ state.level++; if (msg_verbose) MSG_LOG_STATE(myname, state); /* * Don't deliver trace-only requests. */ if (DEL_REQ_TRACE_ONLY(state.request->flags)) { dsb_simple(why, "2.0.0", "delivers to maildir"); return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr))); } /* * Initialize. Assume the operation will fail. Set the delivered * attribute to reflect the final recipient. */ if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0) msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp)); state.msg_attr.delivered = state.msg_attr.rcpt.address; mail_copy_status = MAIL_COPY_STAT_WRITE; buf = vstring_alloc(100); copy_flags = MAIL_COPY_TOFILE | MAIL_COPY_RETURN_PATH | MAIL_COPY_DELIVERED | MAIL_COPY_ORIG_RCPT; newdir = concatenate(usr_attr.mailbox, "new/", (char *) 0); tmpdir = concatenate(usr_attr.mailbox, "tmp/", (char *) 0); curdir = concatenate(usr_attr.mailbox, "cur/", (char *) 0); /* * Create and write the file as the recipient, so that file quota work. * Create any missing directories on the fly. The file name is chosen * according to ftp://koobera.math.uic.edu/www/proto/maildir.html: * * "A unique name has three pieces, separated by dots. On the left is the * result of time(). On the right is the result of gethostname(). In the * middle is something that doesn't repeat within one second on a single * host. I fork a new process for each delivery, so I just use the * process ID. If you're delivering several messages from one process, * use starttime.pid_count.host, where starttime is the time that your * process started, and count is the number of messages you've * delivered." * * Well, that stopped working on fast machines, and on operating systems * that randomize process ID values. When creating a file in tmp/ we use * the process ID because it still is an exclusive resource. When moving * the file to new/ we use the device number and inode number. I do not * care if this breaks on a remote AFS file system, because people should * know better. * * On January 26, 2003, http://cr.yp.to/proto/maildir.html said: * * A unique name has three pieces, separated by dots. On the left is the * result of time() or the second counter from gettimeofday(). On the * right is the result of gethostname(). (To deal with invalid host * names, replace / with \057 and : with \072.) In the middle is a * delivery identifier, discussed below. * * [...] * * Modern delivery identifiers are created by concatenating enough of the * following strings to guarantee uniqueness: * * [...] * * In, where n is (in hexadecimal) the UNIX inode number of this file. * Unfortunately, inode numbers aren't always available through NFS. * * Vn, where n is (in hexadecimal) the UNIX device number of this file. * Unfortunately, device numbers aren't always available through NFS. * (Device numbers are also not helpful with the standard UNIX * filesystem: a maildir has to be within a single UNIX device for link() * and rename() to work.) * * Mn, where n is (in decimal) the microsecond counter from the same * gettimeofday() used for the left part of the unique name. * * Pn, where n is (in decimal) the process ID. * * [...] */ set_eugid(usr_attr.uid, usr_attr.gid); vstring_sprintf(buf, "%lu.P%d.%s", (unsigned long) starttime.tv_sec, var_pid, get_hostname()); tmpfile = concatenate(tmpdir, STR(buf), (char *) 0); newfile = 0; if ((dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0 && (errno != ENOENT || make_dirs(tmpdir, 0700) < 0 || (dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0)) { dsb_simple(why, mbox_dsn(errno, "4.2.0"), "create maildir file %s: %m", tmpfile); } else if (fstat(vstream_fileno(dst), &st) < 0) { /* * Coverity 200604: file descriptor leak in code that never executes. * Code replaced by msg_fatal(), as it is not worthwhile to continue * after an impossible error condition. */ msg_fatal("fstat %s: %m", tmpfile); } else { vstring_sprintf(buf, "%lu.V%lxI%lxM%lu.%s", (unsigned long) starttime.tv_sec, (unsigned long) st.st_dev, (unsigned long) st.st_ino, (unsigned long) starttime.tv_usec, get_hostname()); newfile = concatenate(newdir, STR(buf), (char *) 0); if ((mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), dst, copy_flags, "\n", why)) == 0) { if (sane_link(tmpfile, newfile) < 0 && (errno != ENOENT || (make_dirs(curdir, 0700), make_dirs(newdir, 0700)) < 0 || sane_link(tmpfile, newfile) < 0)) { dsb_simple(why, mbox_dsn(errno, "4.2.0"), "create maildir file %s: %m", newfile); mail_copy_status = MAIL_COPY_STAT_WRITE; } } if (unlink(tmpfile) < 0) msg_warn("remove %s: %m", tmpfile); } set_eugid(var_owner_uid, var_owner_gid); /* * The maildir location is controlled by the mail administrator. If * delivery fails, try again later. We would just bounce when the maildir * location possibly under user control. */ if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) { deliver_status = DEL_STAT_DEFER; } else if (mail_copy_status != 0) { if (errno == EACCES) { msg_warn("maildir access problem for UID/GID=%lu/%lu: %s", (long) usr_attr.uid, (long) usr_attr.gid, STR(why->reason)); msg_warn("perhaps you need to create the maildirs in advance"); } vstring_sprintf_prepend(why->reason, "maildir delivery failed: "); deliver_status = (STR(why->status)[0] == '4' ? defer_append : bounce_append) (BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr)); } else { dsb_simple(why, "2.0.0", "delivered to maildir"); deliver_status = sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr)); } vstring_free(buf); myfree(newdir); myfree(tmpdir); myfree(curdir); myfree(tmpfile); if (newfile) myfree(newfile); return (deliver_status); }
/* * Runs test and count average time (if the nloops is grater than 1) * * returns: -1 = error, 0 = success */ int virtTestRun(const char *title, int nloops, int (*body)(const void *data), const void *data) { int i, ret = 0; double *ts = NULL; testCounter++; if (testOOM < 2) { if (virTestGetVerbose()) fprintf(stderr, "%2d) %-65s ... ", testCounter, title); } if (nloops > 1 && (ts = calloc(nloops, sizeof(double)))==NULL) return -1; for (i=0; i < nloops; i++) { struct timeval before, after; if (ts) GETTIMEOFDAY(&before); virResetLastError(); ret = body(data); virErrorPtr err = virGetLastError(); if (err) { if (virTestGetVerbose() || virTestGetDebug()) virDispatchError(NULL); } if (ret != 0) { break; } if (ts) { GETTIMEOFDAY(&after); ts[i] = DIFF_MSEC(&after, &before); } } if (testOOM < 2) { if (virTestGetVerbose()) { if (ret == 0 && ts) fprintf(stderr, "OK [%.5f ms]\n", virtTestCountAverage(ts, nloops)); else if (ret == 0) fprintf(stderr, "OK\n"); else fprintf(stderr, "FAILED\n"); } else { if (testCounter != 1 && !((testCounter-1) % 40)) { fprintf(stderr, " %-3d\n", (testCounter-1)); fprintf(stderr, " "); } if (ret == 0) fprintf(stderr, "."); else fprintf(stderr, "!"); } } free(ts); return ret; }
/* CENTRY */ int GLUTAPIENTRY glutGet(GLenum param) { Window win, root; int x, y, value; unsigned int width, height, border, depth; switch (param) { case GLUT_INIT_WINDOW_X: return __glutInitX; case GLUT_INIT_WINDOW_Y: return __glutInitY; case GLUT_INIT_WINDOW_WIDTH: return __glutInitWidth; case GLUT_INIT_WINDOW_HEIGHT: return __glutInitHeight; case GLUT_INIT_DISPLAY_MODE: return __glutDisplayMode; case GLUT_WINDOW_X: XTranslateCoordinates(__glutDisplay, __glutCurrentWindow->win, __glutRoot, 0, 0, &x, &y, &win); return x; case GLUT_WINDOW_Y: XTranslateCoordinates(__glutDisplay, __glutCurrentWindow->win, __glutRoot, 0, 0, &x, &y, &win); return y; case GLUT_WINDOW_WIDTH: if (!__glutCurrentWindow->reshape) { XGetGeometry(__glutDisplay, __glutCurrentWindow->win, &root, &x, &y, &width, &height, &border, &depth); return width; } return __glutCurrentWindow->width; case GLUT_WINDOW_HEIGHT: if (!__glutCurrentWindow->reshape) { XGetGeometry(__glutDisplay, __glutCurrentWindow->win, &root, &x, &y, &width, &height, &border, &depth); return height; } return __glutCurrentWindow->height; #define GET_CONFIG(attrib) { \ if (__glutCurrentWindow->renderWin == __glutCurrentWindow->win) { \ glXGetConfig(__glutDisplay, __glutCurrentWindow->vis, \ attrib, &value); \ } else { \ glXGetConfig(__glutDisplay, __glutCurrentWindow->overlay->vis, \ attrib, &value); \ } \ } case GLUT_WINDOW_BUFFER_SIZE: GET_CONFIG(GLX_BUFFER_SIZE); return value; case GLUT_WINDOW_STENCIL_SIZE: GET_CONFIG(GLX_STENCIL_SIZE); return value; case GLUT_WINDOW_DEPTH_SIZE: GET_CONFIG(GLX_DEPTH_SIZE); return value; case GLUT_WINDOW_RED_SIZE: GET_CONFIG(GLX_RED_SIZE); return value; case GLUT_WINDOW_GREEN_SIZE: GET_CONFIG(GLX_GREEN_SIZE); return value; case GLUT_WINDOW_BLUE_SIZE: GET_CONFIG(GLX_BLUE_SIZE); return value; case GLUT_WINDOW_ALPHA_SIZE: GET_CONFIG(GLX_ALPHA_SIZE); return value; case GLUT_WINDOW_ACCUM_RED_SIZE: GET_CONFIG(GLX_ACCUM_RED_SIZE); return value; case GLUT_WINDOW_ACCUM_GREEN_SIZE: GET_CONFIG(GLX_ACCUM_GREEN_SIZE); return value; case GLUT_WINDOW_ACCUM_BLUE_SIZE: GET_CONFIG(GLX_ACCUM_BLUE_SIZE); return value; case GLUT_WINDOW_ACCUM_ALPHA_SIZE: GET_CONFIG(GLX_ACCUM_ALPHA_SIZE); return value; case GLUT_WINDOW_DOUBLEBUFFER: GET_CONFIG(GLX_DOUBLEBUFFER); return value; case GLUT_WINDOW_RGBA: GET_CONFIG(GLX_RGBA); return value; case GLUT_WINDOW_COLORMAP_SIZE: GET_CONFIG(GLX_RGBA); if (value) { return 0; } else { #if defined(_WIN32) /* KLUDGE: we always assume 256 colors in CI mode on Win32 */ return 256; #else if (__glutCurrentWindow->renderWin == __glutCurrentWindow->win) { return __glutCurrentWindow->vis->visual->map_entries; } else { return __glutCurrentWindow->overlay->vis->visual->map_entries; } #endif /* _WIN32 */ } case GLUT_WINDOW_PARENT: return __glutCurrentWindow->parent ? __glutCurrentWindow->parent->num + 1 : 0; case GLUT_WINDOW_NUM_CHILDREN: { int num = 0; GLUTwindow *children = __glutCurrentWindow->children; while (children) { num++; children = children->siblings; } return num; } case GLUT_WINDOW_NUM_SAMPLES: #if defined(GLX_VERSION_1_1) && defined(GLX_SGIS_multisample) if (__glutIsSupportedByGLX("GLX_SGIS_multisample")) { GET_CONFIG(GLX_SAMPLES_SGIS); return value; } else { return 0; } #else /* Independent of GLX server support, multisampling not supported by GLX client-side. */ return 0; #endif case GLUT_WINDOW_STEREO: GET_CONFIG(GLX_STEREO); return value; case GLUT_WINDOW_CURSOR: return __glutCurrentWindow->cursor; case GLUT_SCREEN_WIDTH: return DisplayWidth(__glutDisplay, __glutScreen); case GLUT_SCREEN_HEIGHT: return DisplayHeight(__glutDisplay, __glutScreen); case GLUT_SCREEN_WIDTH_MM: return DisplayWidthMM(__glutDisplay, __glutScreen); case GLUT_SCREEN_HEIGHT_MM: return DisplayHeightMM(__glutDisplay, __glutScreen); case GLUT_MENU_NUM_ITEMS: return __glutCurrentMenu->num; case GLUT_DISPLAY_MODE_POSSIBLE: { XVisualInfo *vi; Bool dummy, visAlloced; void *fbc; #if defined(_WIN32) /* Our fake glXChooseVisual (which is called by __glutDetermineVisual) needs an HDC to work with, so grab one from the "root" window. */ XHDC = GetDC(GetDesktopWindow()); #endif vi = __glutDetermineWindowVisual(&dummy, &visAlloced, &fbc); #if defined(_WIN32) ReleaseDC(GetDesktopWindow(), XHDC); #endif if (vi) { if (visAlloced) XFree(vi); return 1; } return 0; } case GLUT_ELAPSED_TIME: { #ifdef OLD_VMS struct timeval6 elapsed, beginning, now; #else struct timeval elapsed, beginning, now; #endif __glutInitTime(&beginning); GETTIMEOFDAY(&now); TIMEDELTA(elapsed, now, beginning); /* Return elapsed milliseconds. */ #if defined(__vms) && ( __VMS_VER < 70000000 ) return (int) (elapsed.val / TICKS_PER_MILLISECOND); #else return (int) ((elapsed.tv_sec * 1000) + (elapsed.tv_usec / 1000)); #endif } case GLUT_WINDOW_FORMAT_ID: #if defined(_WIN32) return GetPixelFormat(__glutCurrentWindow->hdc); #else if (__glutCurrentWindow->renderWin == __glutCurrentWindow->win) { return (int) __glutCurrentWindow->vis->visualid; } else { return (int) __glutCurrentWindow->overlay->vis->visualid; } #endif default: __glutWarning("invalid glutGet parameter: %d", param); return -1; } }
/* The heart of (S)NTP, exchange NTP packets and compute values to correct the local clock */ int on_wire ( struct addrinfo *host, struct addrinfo *bcast ) { char addr_buf[INET6_ADDRSTRLEN]; register int try; SOCKET sock; struct key *pkt_key = NULL; int key_id = 0; struct timeval tv_xmt; struct pkt x_pkt; int error, rpktl, handle_pkt_res; if (ENABLED_OPT(AUTHENTICATION)) { key_id = (int) OPT_ARG(AUTHENTICATION); get_key(key_id, &pkt_key); } for (try=0; try<5; try++) { memset(&r_pkt, 0, sizeof rbuf); error = GETTIMEOFDAY(&tv_xmt, (struct timezone *)NULL); tv_xmt.tv_sec += JAN_1970; #ifdef DEBUG printf("sntp on_wire: Current time sec: %i msec: %i\n", (unsigned int) tv_xmt.tv_sec, (unsigned int) tv_xmt.tv_usec); #endif if (bcast) { create_socket(&sock, (sockaddr_u *)bcast->ai_addr); rpktl = recv_bcst_pkt(sock, &r_pkt, sizeof rbuf, (sockaddr_u *)bcast->ai_addr); closesocket(sock); } else { int pkt_len = generate_pkt(&x_pkt, &tv_xmt, key_id, pkt_key); create_socket(&sock, (sockaddr_u *)host->ai_addr); sendpkt(sock, (sockaddr_u *)host->ai_addr, &x_pkt, pkt_len); rpktl = recvpkt(sock, &r_pkt, sizeof rbuf, &x_pkt); closesocket(sock); } handle_pkt_res = handle_pkt(rpktl, &r_pkt, host); if (handle_pkt_res < 1) return handle_pkt_res; } getnameinfo(host->ai_addr, host->ai_addrlen, addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST); msyslog(LOG_DEBUG, "Received no useable packet from %s!", addr_buf); return -1; } /* Compute the 8 bits for li_vn_mode */ void set_li_vn_mode ( struct pkt *spkt, char leap, char version, char mode ) { if (leap > 3) { msyslog(LOG_DEBUG, "set_li_vn_mode: leap > 3 using max. 3"); leap = 3; } if (mode > 7) { msyslog(LOG_DEBUG, "set_li_vn_mode: mode > 7, using client mode 3"); mode = 3; } spkt->li_vn_mode = leap << 6; spkt->li_vn_mode |= version << 3; spkt->li_vn_mode |= mode; } /* set_time corrects the local clock by offset with either settimeofday() or by default * with adjtime()/adjusttimeofday(). */ int set_time( double offset ) { struct timeval tp; if (ENABLED_OPT(SETTOD)) { GETTIMEOFDAY(&tp, NULL); tp.tv_sec += (long)offset; tp.tv_usec += 1e6 * (offset - (long)offset); NORMALIZE_TIMEVAL(tp); if (SETTIMEOFDAY(&tp, NULL) < 0) { msyslog(LOG_ERR, "Time not set: settimeofday(): %m"); return -1; } return 0; } tp.tv_sec = (long)offset; tp.tv_usec = 1e6 * (offset - (long)offset); NORMALIZE_TIMEVAL(tp); if (ADJTIMEOFDAY(&tp, NULL) < 0) { msyslog(LOG_ERR, "Time not set: adjtime(): %m"); return -1; } return 0; }
int tls_bio(int fd, int timeout, TLS_SESS_STATE *TLScontext, int (*hsfunc) (SSL *), int (*rfunc) (SSL *, void *, int), int (*wfunc) (SSL *, const void *, int), void *buf, int num) { const char *myname = "tls_bio"; int status; int err; int enable_deadline; struct timeval time_left; /* amount of time left */ struct timeval time_deadline; /* time of deadline */ struct timeval time_now; /* time after SSL_mumble() call */ /* * Compensation for interface mis-match: With VSTREAMs, timeout <= 0 * means wait forever; with the read/write_wait() calls below, we need to * specify timeout < 0 instead. * * Safety: no time limit means no deadline. */ if (timeout <= 0) { timeout = -1; enable_deadline = 0; } /* * Deadline management is simpler than with VSTREAMs, because we don't * need to decrement a per-stream time limit. We just work within the * budget that is available for this tls_bio() call. */ else { enable_deadline = vstream_fstat(TLScontext->stream, VSTREAM_FLAG_DEADLINE); if (enable_deadline) { GETTIMEOFDAY(&time_deadline); time_deadline.tv_sec += timeout; } } /* * If necessary, retry the SSL handshake or read/write operation after * handling any pending network I/O. */ for (;;) { if (hsfunc) status = hsfunc(TLScontext->con); else if (rfunc) status = rfunc(TLScontext->con, buf, num); else if (wfunc) status = wfunc(TLScontext->con, buf, num); else msg_panic("%s: nothing to do here", myname); err = SSL_get_error(TLScontext->con, status); #if (OPENSSL_VERSION_NUMBER <= 0x0090581fL) /* * There is a bug up to and including OpenSSL-0.9.5a: if an error * occurs while checking the peers certificate due to some * certificate error (e.g. as happend with a RSA-padding error), the * error is put onto the error stack. If verification is not * enforced, this error should be ignored, but the error-queue is not * cleared, so we can find this error here. The bug has been fixed on * May 28, 2000. * * This bug so far has only manifested as 4800:error:0407006A:rsa * routines:RSA_padding_check_PKCS1_type_1:block type is not * 01:rsa_pk1.c:100: 4800:error:04067072:rsa * routines:RSA_EAY_PUBLIC_DECRYPT:padding check * failed:rsa_eay.c:396: 4800:error:0D079006:asn1 encoding * routines:ASN1_verify:bad get asn1 object call:a_verify.c:109: so * that we specifically test for this error. We print the errors to * the logfile and automatically clear the error queue. Then we retry * to get another error code. We cannot do better, since we can only * retrieve the last entry of the error-queue without actually * cleaning it on the way. * * This workaround is secure, as verify_result is set to "failed" * anyway. */ if (err == SSL_ERROR_SSL) { if (ERR_peek_error() == 0x0407006AL) { tls_print_errors(); msg_info("OpenSSL <= 0.9.5a workaround called: certificate errors ignored"); err = SSL_get_error(TLScontext->con, status); } } #endif /* * Correspondence between SSL_ERROR_* error codes and tls_bio_(read, * write, accept, connect, shutdown) return values (for brevity: * retval). * * SSL_ERROR_NONE corresponds with retval > 0. With SSL_(read, write) * this is the number of plaintext bytes sent or received. With * SSL_(accept, connect, shutdown) this means that the operation was * completed successfully. * * SSL_ERROR_WANT_(WRITE, READ) start a new loop iteration, or force * (retval = -1, errno = ETIMEDOUT) when the time limit is exceeded. * * All other SSL_ERROR_* cases correspond with retval <= 0. With * SSL_(read, write, accept, connect) retval == 0 means that the * remote party either closed the network connection or that it * requested TLS shutdown; with SSL_shutdown() retval == 0 means that * our own shutdown request is in progress. With all operations * retval < 0 means that there was an error. In the latter case, * SSL_ERROR_SYSCALL means that error details are returned via the * errno value. * * Find out if we must retry the operation and/or if there is pending * network I/O. * * XXX If we're the first to invoke SSL_shutdown(), then the operation * isn't really complete when the call returns. We could hide that * anomaly here and repeat the call. */ switch (err) { case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_READ: if (enable_deadline) { GETTIMEOFDAY(&time_now); timersub(&time_deadline, &time_now, &time_left); timeout = time_left.tv_sec + (time_left.tv_usec > 0); if (timeout <= 0) { errno = ETIMEDOUT; return (-1); } } if (err == SSL_ERROR_WANT_WRITE) { if (write_wait(fd, timeout) < 0) return (-1); /* timeout error */ } else { if (read_wait(fd, timeout) < 0) return (-1); /* timeout error */ } break; /* * Unhandled cases: SSL_ERROR_WANT_(ACCEPT, CONNECT, X509_LOOKUP) * etc. Historically, Postfix silently treated these as ordinary * I/O errors so we don't really know how common they are. For * now, we just log a warning. */ default: msg_warn("%s: unexpected SSL_ERROR code %d", myname, err); /* FALLTHROUGH */ /* * With tls_timed_read() and tls_timed_write() the caller is the * VSTREAM library module which is unaware of TLS, so we log the * TLS error stack here. In a better world, each VSTREAM I/O * object would provide an error reporting method in addition to * the timed_read and timed_write methods, so that we would not * need to have ad-hoc code like this. */ case SSL_ERROR_SSL: if (rfunc || wfunc) tls_print_errors(); /* FALLTHROUGH */ case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_NONE: errno = 0; /* avoid bogus warnings */ /* FALLTHROUGH */ case SSL_ERROR_SYSCALL: return (status); } } }