/*
Unix SMB/CIFS implementation.
+
SMB client socket context management functions
- Copyright (C) Andrew Tridgell 1994-2003
+
+ Copyright (C) Andrew Tridgell 1994-2005
Copyright (C) James Myers 2003 <myersjj@samba.org>
This program is free software; you can redistribute it and/or modify
*/
#include "includes.h"
+#include "lib/events/events.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/composite/composite.h"
+#include "lib/socket/socket.h"
+
+/*
+ this private structure is used during async connection handling
+*/
+struct clisocket_connect {
+ int port_num;
+ int *iports;
+ struct smbcli_socket *sock;
+ const char *dest_host;
+};
/*
- create a cli_socket context
+ create a smbcli_socket context
+ The event_ctx is optional - if not supplied one will be created
*/
-struct cli_socket *cli_sock_init(void)
+struct smbcli_socket *smbcli_sock_init(TALLOC_CTX *mem_ctx,
+ struct event_context *event_ctx)
{
- struct cli_socket *sock;
- TALLOC_CTX *mem_ctx;
+ struct smbcli_socket *sock;
- mem_ctx = talloc_init("cli_socket");
- if (!mem_ctx) return NULL;
-
- sock = talloc_zero(mem_ctx, sizeof(*sock));
+ sock = talloc_zero(mem_ctx, struct smbcli_socket);
if (!sock) {
- talloc_destroy(mem_ctx);
return NULL;
}
- sock->mem_ctx = mem_ctx;
- sock->fd = -1;
- sock->port = 0;
- /* 20 second default timeout */
- sock->timeout = 20000;
-
- sock->hostname = NULL;
+ if (event_ctx) {
+ sock->event.ctx = talloc_reference(sock, event_ctx);
+ } else {
+ sock->event.ctx = event_context_init(sock);
+ }
+ if (sock->event.ctx == NULL) {
+ talloc_free(sock);
+ return NULL;
+ }
return sock;
}
+static NTSTATUS smbcli_sock_connect_one(struct smbcli_socket *sock,
+ const char *hostaddr, int port,
+ struct composite_context *c);
+
/*
- connect a cli_socket context to an IP/port pair
- if port is 0 then choose 445 then 139
+ handle socket write events during an async connect. These happen when the OS
+ has either completed the connect() or has returned an error
*/
-BOOL cli_sock_connect(struct cli_socket *sock, struct in_addr *ip, int port)
+static void smbcli_sock_connect_handler(struct event_context *ev, struct fd_event *fde,
+ uint16_t flags, void *private)
{
- if (getenv("LIBSMB_PROG")) {
- sock->fd = sock_exec(getenv("LIBSMB_PROG"));
- return sock->fd != -1;
+ struct composite_context *c = talloc_get_type(private, struct composite_context);
+ struct clisocket_connect *conn = talloc_get_type(c->private, struct clisocket_connect);
+ int i;
+
+ c->status = socket_connect_complete(conn->sock->sock, 0);
+ if (NT_STATUS_IS_OK(c->status)) {
+ socket_set_option(conn->sock->sock, lp_socket_options(), NULL);
+ conn->sock->hostname = talloc_strdup(conn->sock, conn->dest_host);
+ c->state = SMBCLI_REQUEST_DONE;
+ if (c->async.fn) {
+ c->async.fn(c);
+ }
+ return;
}
- if (port == 0) {
- return cli_sock_connect(sock, ip, 445) ||
- cli_sock_connect(sock, ip, 139);
+ /* that port failed - try the next port */
+ for (i=conn->port_num+1;conn->iports[i];i++) {
+ conn->port_num = i;
+ c->status = smbcli_sock_connect_one(conn->sock,
+ conn->dest_host,
+ conn->iports[i], c);
+ if (NT_STATUS_IS_OK(c->status) ||
+ NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ return;
+ }
+ }
+
+ c->state = SMBCLI_REQUEST_ERROR;
+ if (c->async.fn) {
+ c->async.fn(c);
+ }
+}
+
+
+/*
+ try to connect to the given address/port
+*/
+static NTSTATUS smbcli_sock_connect_one(struct smbcli_socket *sock,
+ const char *hostaddr, int port,
+ struct composite_context *c)
+{
+ NTSTATUS status;
+
+ if (sock->sock) {
+ talloc_free(sock->sock);
+ sock->sock = NULL;
+ }
+ talloc_free(sock->event.fde);
+
+ status = socket_create("ip", SOCKET_TYPE_STREAM, &sock->sock, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
}
- sock->dest_ip = *ip;
+ talloc_steal(sock, sock->sock);
+
+ /* we initially look for write - see the man page on
+ non-blocking connect */
+ sock->event.fde = event_add_fd(sock->event.ctx, sock, socket_get_fd(sock->sock),
+ EVENT_FD_WRITE, smbcli_sock_connect_handler, c);
+
sock->port = port;
- sock->fd = open_socket_out(SOCK_STREAM,
- &sock->dest_ip,
- sock->port,
- LONG_CONNECT_TIMEOUT);
- return (sock->fd != -1);
+ set_blocking(socket_get_fd(sock->sock), False);
+
+ return socket_connect(sock->sock, NULL, 0, hostaddr, port, 0);
}
+
+/*
+ connect a smbcli_socket context to an IP/port pair
+ if port is 0 then choose 445 then 139
-/****************************************************************************
- mark the socket as dead
-****************************************************************************/
-void cli_sock_dead(struct cli_socket *sock)
+ this is the async send side of the interface
+*/
+struct composite_context *smbcli_sock_connect_send(struct smbcli_socket *sock,
+ const char *host_addr, int port)
+{
+ struct composite_context *c;
+ struct clisocket_connect *conn;
+ int i;
+
+ c = talloc_zero(sock, struct composite_context);
+ if (c == NULL) return NULL;
+
+ c->event_ctx = sock->event.ctx;
+
+ conn = talloc(c, struct clisocket_connect);
+ if (conn == NULL) goto failed;
+
+ conn->sock = sock;
+
+ /* work out what ports we will try */
+ if (port == 0) {
+ const char **ports = lp_smb_ports();
+ for (i=0;ports[i];i++) /* noop */ ;
+ conn->iports = talloc_array(c, int, i+1);
+ if (conn->iports == NULL) goto failed;
+ for (i=0;ports[i];i++) {
+ conn->iports[i] = atoi(ports[i]);
+ }
+ conn->iports[i] = 0;
+ } else {
+ conn->iports = talloc_array(c, int, 2);
+ if (conn->iports == NULL) goto failed;
+ conn->iports[0] = port;
+ conn->iports[1] = 0;
+ }
+
+ conn->dest_host = talloc_strdup(c, host_addr);
+ if (conn->dest_host == NULL) goto failed;
+
+ c->private = conn;
+ c->state = SMBCLI_REQUEST_SEND;
+
+ /* startup the connect process for each port in turn until one
+ succeeds or tells us that it is pending */
+ for (i=0;conn->iports[i];i++) {
+ conn->port_num = i;
+ conn->sock->port = conn->iports[i];
+ c->status = smbcli_sock_connect_one(sock,
+ conn->dest_host,
+ conn->iports[i], c);
+ if (NT_STATUS_IS_OK(c->status) ||
+ NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ return c;
+ }
+ }
+
+ c->state = SMBCLI_REQUEST_ERROR;
+ return c;
+
+failed:
+ talloc_free(c);
+ return NULL;
+}
+
+/*
+ finish a smbcli_sock_connect_send() operation
+*/
+NTSTATUS smbcli_sock_connect_recv(struct composite_context *c)
+{
+ NTSTATUS status;
+ status = composite_wait(c);
+ talloc_free(c);
+ return status;
+}
+
+/*
+ connect a smbcli_socket context to an IP/port pair
+ if port is 0 then choose the ports listed in smb.conf (normally 445 then 139)
+
+ sync version of the function
+*/
+NTSTATUS smbcli_sock_connect(struct smbcli_socket *sock, const char *host_addr, int port)
{
- if (sock->fd != -1) {
- close(sock->fd);
- sock->fd = -1;
+ struct composite_context *c;
+
+ c = smbcli_sock_connect_send(sock, host_addr, port);
+ if (c == NULL) {
+ return NT_STATUS_NO_MEMORY;
}
+
+ return smbcli_sock_connect_recv(c);
}
+
/****************************************************************************
- reduce socket reference count - if it becomes zero then close
+ mark the socket as dead
****************************************************************************/
-void cli_sock_close(struct cli_socket *sock)
+void smbcli_sock_dead(struct smbcli_socket *sock)
{
- sock->reference_count--;
- if (sock->reference_count <= 0) {
- cli_sock_dead(sock);
+ if (sock->sock != NULL) {
+ talloc_free(sock->sock);
+ sock->sock = NULL;
}
}
/****************************************************************************
Set socket options on a open connection.
****************************************************************************/
-void cli_sock_set_options(struct cli_socket *sock, const char *options)
+void smbcli_sock_set_options(struct smbcli_socket *sock, const char *options)
{
- set_socket_options(sock->fd, options);
+ socket_set_option(sock->sock, options, NULL);
}
/****************************************************************************
Write to socket. Return amount written.
****************************************************************************/
-ssize_t cli_sock_write(struct cli_socket *sock, const char *data, size_t len)
+ssize_t smbcli_sock_write(struct smbcli_socket *sock, const uint8_t *data, size_t len)
{
- if (sock->fd == -1) {
+ NTSTATUS status;
+ DATA_BLOB blob;
+ size_t nsent;
+
+ if (sock->sock == NULL) {
errno = EIO;
return -1;
}
- return write_data(sock->fd, data, len);
+ blob.data = discard_const(data);
+ blob.length = len;
+
+ status = socket_send(sock->sock, &blob, &nsent, 0);
+ if (NT_STATUS_IS_ERR(status)) {
+ return -1;
+ }
+
+ return nsent;
}
/****************************************************************************
Read from socket. return amount read
****************************************************************************/
-ssize_t cli_sock_read(struct cli_socket *sock, char *data, size_t len)
+ssize_t smbcli_sock_read(struct smbcli_socket *sock, uint8_t *data, size_t len)
{
- if (sock->fd == -1) {
+ NTSTATUS status;
+ size_t nread;
+
+ if (sock->sock == NULL) {
errno = EIO;
return -1;
}
- return read_data(sock->fd, data, len);
+ status = socket_recv(sock->sock, data, len, &nread, 0);
+ if (NT_STATUS_IS_ERR(status)) {
+ return -1;
+ }
+
+ return nread;
}
+
/****************************************************************************
resolve a hostname and connect
****************************************************************************/
-BOOL cli_sock_connect_byname(struct cli_socket *sock, const char *host, int port)
+BOOL smbcli_sock_connect_byname(struct smbcli_socket *sock, const char *host, int port)
{
- int name_type = 0x20;
- struct in_addr ip;
- TALLOC_CTX *mem_ctx;
+ int name_type = NBT_NAME_SERVER;
+ const char *address;
+ NTSTATUS status;
+ struct nbt_name nbt_name;
char *name, *p;
- BOOL ret;
- if (getenv("LIBSMB_PROG")) {
- sock->fd = sock_exec(getenv("LIBSMB_PROG"));
- return sock->fd != -1;
- }
-
- mem_ctx = talloc_init("cli_sock_connect_byname");
- if (!mem_ctx) return False;
-
- name = talloc_strdup(mem_ctx, host);
+ name = talloc_strdup(sock, host);
/* allow hostnames of the form NAME#xx and do a netbios lookup */
if ((p = strchr(name, '#'))) {
*p = 0;
}
- if (!resolve_name(mem_ctx, name, &ip, name_type)) {
- talloc_destroy(mem_ctx);
+ nbt_name.name = name;
+ nbt_name.type = name_type;
+ nbt_name.scope = NULL;
+
+ status = resolve_name(&nbt_name, sock, &address);
+ if (!NT_STATUS_IS_OK(status)) {
return False;
}
- ret = cli_sock_connect(sock, &ip, port);
-
- if (ret) {
- sock->hostname = talloc_steal(mem_ctx, sock->mem_ctx, name);
- }
+ sock->hostname = name;
- talloc_destroy(mem_ctx);
+ status = smbcli_sock_connect(sock, address, port);
- return ret;
+ return NT_STATUS_IS_OK(status);
}