}
#endif
- zero_addr(pss);
+ zero_sockaddr(pss);
if (!interpret_string_addr_internal(&res, str, flags|AI_ADDRCONFIG)) {
return false;
Set an address to INADDR_ANY.
******************************************************************/
-void zero_addr(struct sockaddr_storage *pss)
+void zero_sockaddr(struct sockaddr_storage *pss)
{
memset(pss, '\0', sizeof(*pss));
/* Ensure we're at least a valid sockaddr-storage. */
return print_sockaddr_len(addr_buf, addr_len, (struct sockaddr *)&sa, length);
}
-#if 0
-/* Not currently used. JRA. */
/****************************************************************************
Return the port number we've bound to on a socket.
****************************************************************************/
-static int get_socket_port(int fd)
+int get_socket_port(int fd)
{
struct sockaddr_storage sa;
socklen_t length = sizeof(sa);
}
return -1;
}
-#endif
const char *client_name(int fd)
{
* leak in SCO Openserver 5.0 */
/* reported on samba-technical --jerry */
if ( DEBUGLEVEL >= 5 ) {
+ DEBUG(5,("Socket options:\n"));
for (; p->name != NULL; p++) {
if (getsockopt(s, p->level, p->option,
(void *)&value, &vlen) == -1) {
- DEBUG(5,("Could not test socket option %s.\n",
+ DEBUGADD(5,("\tCould not test socket option %s.\n",
p->name));
} else {
- DEBUG(5,("socket option %s = %d\n",
+ DEBUGADD(5,("\t%s = %d\n",
p->name,value));
}
}
}
while (nread < mincnt) {
- readret = sys_read(fd, buf + nread, maxcnt - nread);
+ readret = sys_recv(fd, buf + nread, maxcnt - nread, 0);
if (readret == 0) {
DEBUG(5,("read_socket_with_timeout: "
return NT_STATUS_IO_TIMEOUT;
}
- readret = sys_read(fd, buf+nread, maxcnt-nread);
+ readret = sys_recv(fd, buf+nread, maxcnt-nread, 0);
if (readret == 0) {
/* we got EOF on the file descriptor */
}
/****************************************************************************
- Write data to a fd.
+ Write all data from an iov array
****************************************************************************/
-ssize_t write_data(int fd, const char *buffer, size_t N)
+ssize_t write_data_iov(int fd, const struct iovec *orig_iov, int iovcnt)
{
- size_t total=0;
- ssize_t ret;
- char addr[INET6_ADDRSTRLEN];
+ int i;
+ size_t to_send;
+ ssize_t thistime;
+ size_t sent;
+ struct iovec *iov_copy, *iov;
- while (total < N) {
- ret = sys_write(fd,buffer + total,N - total);
+ to_send = 0;
+ for (i=0; i<iovcnt; i++) {
+ to_send += orig_iov[i].iov_len;
+ }
- if (ret == -1) {
- if (fd == get_client_fd()) {
- /* Try and give an error message saying
- * what client failed. */
- DEBUG(0,("write_data: write failure in "
- "writing to client %s. Error %s\n",
- get_peer_addr(fd,addr,sizeof(addr)),
- strerror(errno) ));
- } else {
- DEBUG(0,("write_data: write failure. "
- "Error = %s\n", strerror(errno) ));
+ thistime = sys_writev(fd, orig_iov, iovcnt);
+ if ((thistime <= 0) || (thistime == to_send)) {
+ return thistime;
+ }
+ sent = thistime;
+
+ /*
+ * We could not send everything in one call. Make a copy of iov that
+ * we can mess with. We keep a copy of the array start in iov_copy for
+ * the TALLOC_FREE, because we're going to modify iov later on,
+ * discarding elements.
+ */
+
+ iov_copy = (struct iovec *)TALLOC_MEMDUP(
+ talloc_tos(), orig_iov, sizeof(struct iovec) * iovcnt);
+
+ if (iov_copy == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ iov = iov_copy;
+
+ while (sent < to_send) {
+ /*
+ * We have to discard "thistime" bytes from the beginning
+ * iov array, "thistime" contains the number of bytes sent
+ * via writev last.
+ */
+ while (thistime > 0) {
+ if (thistime < iov[0].iov_len) {
+ char *new_base =
+ (char *)iov[0].iov_base + thistime;
+ iov[0].iov_base = new_base;
+ iov[0].iov_len -= thistime;
+ break;
}
- return -1;
+ thistime -= iov[0].iov_len;
+ iov += 1;
+ iovcnt -= 1;
}
- if (ret == 0) {
- return total;
+ thistime = sys_writev(fd, iov, iovcnt);
+ if (thistime <= 0) {
+ break;
}
+ sent += thistime;
+ }
+
+ TALLOC_FREE(iov_copy);
+ return sent;
+}
+
+/****************************************************************************
+ Write data to a fd.
+****************************************************************************/
+
+ssize_t write_data(int fd, const char *buffer, size_t N)
+{
+ ssize_t ret;
+ struct iovec iov;
+
+ iov.iov_base = CONST_DISCARD(char *, buffer);
+ iov.iov_len = N;
+
+ ret = write_data_iov(fd, &iov, 1);
+ if (ret >= 0) {
+ return ret;
+ }
- total += ret;
+ if (fd == get_client_fd()) {
+ char addr[INET6_ADDRSTRLEN];
+ /*
+ * Try and give an error message saying what client failed.
+ */
+ DEBUG(0, ("write_data: write failure in writing to client %s. "
+ "Error %s\n", get_peer_addr(fd,addr,sizeof(addr)),
+ strerror(errno)));
+ } else {
+ DEBUG(0,("write_data: write failure. Error = %s\n",
+ strerror(errno) ));
}
- return (ssize_t)total;
+
+ return -1;
}
/****************************************************************************
return( res );
}
+struct open_socket_out_state {
+ int fd;
+ struct event_context *ev;
+ struct sockaddr_storage ss;
+ socklen_t salen;
+ uint16_t port;
+ int wait_nsec;
+};
+
+static void open_socket_out_connected(struct tevent_req *subreq);
+
+static int open_socket_out_state_destructor(struct open_socket_out_state *s)
+{
+ if (s->fd != -1) {
+ close(s->fd);
+ }
+ return 0;
+}
+
/****************************************************************************
Create an outgoing socket. timeout is in milliseconds.
**************************************************************************/
-int open_socket_out(int type,
- const struct sockaddr_storage *pss,
- uint16_t port,
- int timeout)
+struct tevent_req *open_socket_out_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ const struct sockaddr_storage *pss,
+ uint16_t port,
+ int timeout)
{
char addr[INET6_ADDRSTRLEN];
- struct sockaddr_storage sock_out = *pss;
- int res,ret;
- int connect_loop = 10;
- int increment = 10;
+ struct tevent_req *result, *subreq;
+ struct open_socket_out_state *state;
+ NTSTATUS status;
- /* create a socket to write to */
- res = socket(pss->ss_family, type, 0);
- if (res == -1) {
- DEBUG(0,("socket error (%s)\n", strerror(errno)));
- return -1;
+ result = tevent_req_create(mem_ctx, &state,
+ struct open_socket_out_state);
+ if (result == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->ss = *pss;
+ state->port = port;
+ state->wait_nsec = 10000;
+ state->salen = -1;
+
+ state->fd = socket(state->ss.ss_family, SOCK_STREAM, 0);
+ if (state->fd == -1) {
+ status = map_nt_error_from_unix(errno);
+ goto post_status;
}
+ talloc_set_destructor(state, open_socket_out_state_destructor);
- if (type != SOCK_STREAM) {
- return res;
+ if (!tevent_req_set_endtime(
+ result, ev, timeval_current_ofs(0, timeout*1000))) {
+ goto fail;
}
#if defined(HAVE_IPV6)
if (pss->ss_family == AF_INET6) {
- struct sockaddr_in6 *psa6 = (struct sockaddr_in6 *)&sock_out;
+ struct sockaddr_in6 *psa6;
+ psa6 = (struct sockaddr_in6 *)&state->ss;
psa6->sin6_port = htons(port);
- if (psa6->sin6_scope_id == 0 &&
- IN6_IS_ADDR_LINKLOCAL(&psa6->sin6_addr)) {
- setup_linklocal_scope_id((struct sockaddr *)&sock_out);
+ if (psa6->sin6_scope_id == 0
+ && IN6_IS_ADDR_LINKLOCAL(&psa6->sin6_addr)) {
+ setup_linklocal_scope_id(
+ (struct sockaddr *)&(state->ss));
}
+ state->salen = sizeof(struct sockaddr_in6);
}
#endif
if (pss->ss_family == AF_INET) {
- struct sockaddr_in *psa = (struct sockaddr_in *)&sock_out;
+ struct sockaddr_in *psa;
+ psa = (struct sockaddr_in *)&state->ss;
psa->sin_port = htons(port);
- }
+ state->salen = sizeof(struct sockaddr_in);
+ }
+
+ print_sockaddr(addr, sizeof(addr), &state->ss);
+ DEBUG(3,("Connecting to %s at port %u\n", addr, (unsigned int)port));
+
+ subreq = async_connect_send(state, state->ev, state->fd,
+ (struct sockaddr *)&state->ss,
+ state->salen);
+ if ((subreq == NULL)
+ || !tevent_req_set_endtime(
+ subreq, state->ev,
+ timeval_current_ofs(0, state->wait_nsec))) {
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, open_socket_out_connected, result);
+ return result;
+
+ post_status:
+ tevent_req_nterror(result, status);
+ return tevent_req_post(result, ev);
+ fail:
+ TALLOC_FREE(result);
+ return NULL;
+}
- /* set it non-blocking */
- set_blocking(res,false);
+static void open_socket_out_connected(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct open_socket_out_state *state =
+ tevent_req_data(req, struct open_socket_out_state);
+ int ret;
+ int sys_errno;
- print_sockaddr(addr, sizeof(addr), &sock_out);
- DEBUG(3,("Connecting to %s at port %u\n",
- addr,
- (unsigned int)port));
+ ret = async_connect_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == 0) {
+ tevent_req_done(req);
+ return;
+ }
- /* and connect it to the destination */
- connect_again:
+ if (
+#ifdef ETIMEDOUT
+ (sys_errno == ETIMEDOUT) ||
+#endif
+ (sys_errno == EINPROGRESS) ||
+ (sys_errno == EALREADY) ||
+ (sys_errno == EAGAIN)) {
- ret = sys_connect(res, (struct sockaddr *)&sock_out);
+ /*
+ * retry
+ */
- /* Some systems return EAGAIN when they mean EINPROGRESS */
- if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY ||
- errno == EAGAIN) && (connect_loop < timeout) ) {
- smb_msleep(connect_loop);
- timeout -= connect_loop;
- connect_loop += increment;
- if (increment < 250) {
- /* After 8 rounds we end up at a max of 255 msec */
- increment *= 1.5;
+ if (state->wait_nsec < 250000) {
+ state->wait_nsec *= 1.5;
}
- goto connect_again;
- }
- if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY ||
- errno == EAGAIN)) {
- DEBUG(1,("timeout connecting to %s:%u\n",
- addr,
- (unsigned int)port));
- close(res);
- return -1;
+ subreq = async_connect_send(state, state->ev, state->fd,
+ (struct sockaddr *)&state->ss,
+ state->salen);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ if (!tevent_req_set_endtime(
+ subreq, state->ev,
+ timeval_current_ofs(0, state->wait_nsec))) {
+ tevent_req_nterror(req, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ tevent_req_set_callback(subreq, open_socket_out_connected, req);
+ return;
}
#ifdef EISCONN
- if (ret < 0 && errno == EISCONN) {
- errno = 0;
- ret = 0;
+ if (sys_errno == EISCONN) {
+ tevent_req_done(req);
+ return;
}
#endif
- if (ret < 0) {
- DEBUG(2,("error connecting to %s:%d (%s)\n",
- addr,
- (unsigned int)port,
- strerror(errno)));
- close(res);
- return -1;
+ /* real error */
+ tevent_req_nterror(req, map_nt_error_from_unix(sys_errno));
+}
+
+NTSTATUS open_socket_out_recv(struct tevent_req *req, int *pfd)
+{
+ struct open_socket_out_state *state =
+ tevent_req_data(req, struct open_socket_out_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
}
+ *pfd = state->fd;
+ state->fd = -1;
+ return NT_STATUS_OK;
+}
- /* set it blocking again */
- set_blocking(res,true);
+NTSTATUS open_socket_out(const struct sockaddr_storage *pss, uint16_t port,
+ int timeout, int *pfd)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct event_context *ev;
+ struct tevent_req *req;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;
- return res;
+ ev = event_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ req = open_socket_out_send(frame, ev, pss, port, timeout);
+ if (req == NULL) {
+ goto fail;
+ }
+ if (!tevent_req_poll(req, ev)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto fail;
+ }
+ status = open_socket_out_recv(req, pfd);
+ fail:
+ TALLOC_FREE(frame);
+ return status;
+}
+
+struct open_socket_out_defer_state {
+ struct event_context *ev;
+ struct sockaddr_storage ss;
+ uint16_t port;
+ int timeout;
+ int fd;
+};
+
+static void open_socket_out_defer_waited(struct tevent_req *subreq);
+static void open_socket_out_defer_connected(struct tevent_req *subreq);
+
+struct tevent_req *open_socket_out_defer_send(TALLOC_CTX *mem_ctx,
+ struct event_context *ev,
+ struct timeval wait_time,
+ const struct sockaddr_storage *pss,
+ uint16_t port,
+ int timeout)
+{
+ struct tevent_req *req, *subreq;
+ struct open_socket_out_defer_state *state;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct open_socket_out_defer_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->ss = *pss;
+ state->port = port;
+ state->timeout = timeout;
+
+ subreq = tevent_wakeup_send(
+ state, ev,
+ timeval_current_ofs(wait_time.tv_sec, wait_time.tv_usec));
+ if (subreq == NULL) {
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, open_socket_out_defer_waited, req);
+ return req;
+ fail:
+ TALLOC_FREE(req);
+ return NULL;
+}
+
+static void open_socket_out_defer_waited(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct open_socket_out_defer_state *state = tevent_req_data(
+ req, struct open_socket_out_defer_state);
+ bool ret;
+
+ ret = tevent_wakeup_recv(subreq);
+ TALLOC_FREE(subreq);
+ if (!ret) {
+ tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+ return;
+ }
+
+ subreq = open_socket_out_send(state, state->ev, &state->ss,
+ state->port, state->timeout);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, open_socket_out_defer_connected, req);
+}
+
+static void open_socket_out_defer_connected(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct open_socket_out_defer_state *state = tevent_req_data(
+ req, struct open_socket_out_defer_state);
+ NTSTATUS status;
+
+ status = open_socket_out_recv(subreq, &state->fd);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ tevent_req_nterror(req, status);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+NTSTATUS open_socket_out_defer_recv(struct tevent_req *req, int *pfd)
+{
+ struct open_socket_out_defer_state *state = tevent_req_data(
+ req, struct open_socket_out_defer_state);
+ NTSTATUS status;
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+ *pfd = state->fd;
+ state->fd = -1;
+ return NT_STATUS_OK;
}
/*******************************************************************
if (!res->ai_addr) {
continue;
}
- if (addr_equal((const struct sockaddr *)res->ai_addr,
+ if (sockaddr_equal((const struct sockaddr *)res->ai_addr,
(struct sockaddr *)pss)) {
freeaddrinfo(ailist);
return true;
p = get_peer_addr_internal(fd, addr_buf, sizeof(addr_buf), (struct sockaddr *)&ss, &length);
/* it might be the same as the last one - save some DNS work */
- if (addr_equal((struct sockaddr *)&ss, (struct sockaddr *)&nc.ss)) {
+ if (sockaddr_equal((struct sockaddr *)&ss, (struct sockaddr *)&nc.ss)) {
return nc.name ? nc.name : "UNKNOWN";
}
bool is_myname_or_ipaddr(const char *s)
{
TALLOC_CTX *ctx = talloc_tos();
+ char addr[INET6_ADDRSTRLEN];
char *name = NULL;
const char *dnsname;
char *servername = NULL;
if (!is_ipaddress(servername)) {
/* Use DNS to resolve the name, but only the first address */
struct sockaddr_storage ss;
- if (interpret_string_addr(&ss, servername,0)) {
- print_sockaddr(name,
- sizeof(name),
+ if (interpret_string_addr(&ss, servername, 0)) {
+ print_sockaddr(addr,
+ sizeof(addr),
&ss);
- servername = name;
+ servername = addr;
}
}
return false;
}
+ if (ismyaddr((struct sockaddr *)&ss)) {
+ return true;
+ }
+
if (is_zero_addr((struct sockaddr *)&ss) ||
is_loopback_addr((struct sockaddr *)&ss)) {
return false;
}
n = get_interfaces(nics, MAX_INTERFACES);
for (i=0; i<n; i++) {
- if (addr_equal((struct sockaddr *)&nics[i].ip, (struct sockaddr *)&ss)) {
+ if (sockaddr_equal((struct sockaddr *)&nics[i].ip, (struct sockaddr *)&ss)) {
TALLOC_FREE(nics);
return true;
}
/* No match */
return false;
}
+
+/*
+ * Read an smb packet asynchronously, discard keepalives
+ */
+
+struct read_smb_state {
+ struct tevent_context *ev;
+ int fd;
+ uint8_t *buf;
+};
+
+static ssize_t read_smb_more(uint8_t *buf, size_t buflen, void *private_data);
+static void read_smb_done(struct tevent_req *subreq);
+
+struct tevent_req *read_smb_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ int fd)
+{
+ struct tevent_req *result, *subreq;
+ struct read_smb_state *state;
+
+ result = tevent_req_create(mem_ctx, &state, struct read_smb_state);
+ if (result == NULL) {
+ return NULL;
+ }
+ state->ev = ev;
+ state->fd = fd;
+
+ subreq = read_packet_send(state, ev, fd, 4, read_smb_more, NULL);
+ if (subreq == NULL) {
+ goto fail;
+ }
+ tevent_req_set_callback(subreq, read_smb_done, result);
+ return result;
+ fail:
+ TALLOC_FREE(result);
+ return NULL;
+}
+
+static ssize_t read_smb_more(uint8_t *buf, size_t buflen, void *private_data)
+{
+ if (buflen > 4) {
+ return 0; /* We've been here, we're done */
+ }
+ return smb_len_large(buf);
+}
+
+static void read_smb_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct read_smb_state *state = tevent_req_data(
+ req, struct read_smb_state);
+ ssize_t len;
+ int err;
+
+ len = read_packet_recv(subreq, state, &state->buf, &err);
+ TALLOC_FREE(subreq);
+ if (len == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+
+ if (CVAL(state->buf, 0) == SMBkeepalive) {
+ subreq = read_packet_send(state, state->ev, state->fd, 4,
+ read_smb_more, NULL);
+ if (tevent_req_nomem(subreq, req)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, read_smb_done, req);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+ssize_t read_smb_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ uint8_t **pbuf, int *perrno)
+{
+ struct read_smb_state *state = tevent_req_data(
+ req, struct read_smb_state);
+
+ if (tevent_req_is_unix_error(req, perrno)) {
+ return -1;
+ }
+ *pbuf = talloc_move(mem_ctx, &state->buf);
+ return talloc_get_size(*pbuf);
+}
+
+struct getaddrinfo_state {
+ const char *node;
+ const char *service;
+ const struct addrinfo *hints;
+ struct addrinfo *res;
+ int ret;
+};
+
+static void getaddrinfo_do(void *private_data);
+static void getaddrinfo_done(struct tevent_req *subreq);
+
+struct tevent_req *getaddrinfo_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct fncall_context *ctx,
+ const char *node,
+ const char *service,
+ const struct addrinfo *hints)
+{
+ struct tevent_req *req, *subreq;
+ struct getaddrinfo_state *state;
+
+ req = tevent_req_create(mem_ctx, &state, struct getaddrinfo_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ state->node = node;
+ state->service = service;
+ state->hints = hints;
+
+ subreq = fncall_send(state, ev, ctx, getaddrinfo_do, state);
+ if (tevent_req_nomem(subreq, req)) {
+ return tevent_req_post(req, ev);
+ }
+ tevent_req_set_callback(subreq, getaddrinfo_done, req);
+ return req;
+}
+
+static void getaddrinfo_do(void *private_data)
+{
+ struct getaddrinfo_state *state =
+ (struct getaddrinfo_state *)private_data;
+
+ state->ret = getaddrinfo(state->node, state->service, state->hints,
+ &state->res);
+}
+
+static void getaddrinfo_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ int ret, err;
+
+ ret = fncall_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_req_error(req, err);
+ return;
+ }
+ tevent_req_done(req);
+}
+
+int getaddrinfo_recv(struct tevent_req *req, struct addrinfo **res)
+{
+ struct getaddrinfo_state *state = tevent_req_data(
+ req, struct getaddrinfo_state);
+ int err;
+
+ if (tevent_req_is_unix_error(req, &err)) {
+ switch(err) {
+ case ENOMEM:
+ return EAI_MEMORY;
+ default:
+ return EAI_FAIL;
+ }
+ }
+ if (state->ret == 0) {
+ *res = state->res;
+ }
+ return state->ret;
+}