#include "silc.h"
#include "silcsymbiansocketstream.h"
+/************************ Static utility functions **************************/
+
+static SilcBool silc_net_set_sockaddr(TInetAddr *addr, const char *ip_addr,
+ int port)
+{
+ /* Check for IPv4 and IPv6 addresses */
+ if (ip_addr) {
+ if (!silc_net_is_ip(ip_addr)) {
+ SILC_LOG_ERROR(("%s is not IP address", ip_addr));
+ return FALSE;
+ }
+
+ if (silc_net_is_ip4(ip_addr)) {
+ /* IPv4 address */
+ unsigned char buf[4];
+ TUint32 a;
+
+ if (!silc_net_addr2bin(ip_addr, buf, sizeof(buf)))
+ return FALSE;
+
+ SILC_GET32_MSB(a, buf);
+ addr->SetAddress(a);
+ addr->SetPort(port);
+ } else {
+#ifdef HAVE_IPV6
+ SILC_LOG_ERROR(("IPv6 not supported"));
+ return FALSE;
+#else
+ SILC_LOG_ERROR(("Operating System does not support IPv6"));
+ return FALSE;
+#endif
+ }
+ } else {
+ addr->SetAddress(0);
+ addr->SetPort(port);
+ }
+
+ return TRUE;
+}
+
/****************************** TCP Listener ********************************/
class SilcSymbianTCPListener;
+extern "C" {
+
/* Deliver new stream to upper layer */
-static void silc_net_accept_stream(SilcSocketStreamStatus status,
+static void silc_net_accept_stream(SilcResult status,
SilcStream stream, void *context)
{
SilcNetListener listener = (SilcNetListener)context;
- /* In case of error, the socket has been destroyed already */
- if (status != SILC_SOCKET_OK)
+ /* In case of error, the socket has been destroyed already via
+ silc_stream_destroy. */
+ if (status != SILC_OK)
return;
- listener->callback(SILC_NET_OK, stream, listener->context);
+ listener->callback(SILC_OK, stream, listener->context);
}
+} /* extern "C" */
+
/* TCP Listener class */
class SilcSymbianTCPListener : public CActive {
/* Listen for connection */
void Listen()
{
+ SILC_LOG_DEBUG(("Listen()"));
+
new_conn = new RSocket;
if (!new_conn)
return;
/* Listener callback */
virtual void RunL()
{
+ SILC_LOG_DEBUG(("RunL(), iStatus=%d", iStatus));
+
if (iStatus != KErrNone) {
if (new_conn)
delete new_conn;
return;
}
+ SILC_LOG_DEBUG(("Accept new connection"));
+
/* Set socket options */
new_conn->SetOpt(KSoReuseAddr, KSolInetIp, 1);
SilcNetListener listener;
};
+extern "C" {
+
/* Create TCP listener */
SilcNetListener
SilcSymbianTCPListener *l = NULL;
TInetAddr server;
TInt ret;
- TBuf<64> tmp;
int i;
SILC_LOG_DEBUG(("Creating TCP listener"));
- if (port < 0 || !schedule || !callback)
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ goto err;
+ }
+ }
+
+ if (port < 0 || !callback) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
goto err;
+ }
listener = (SilcNetListener)silc_calloc(1, sizeof(*listener));
if (!listener) {
- callback(SILC_NET_NO_MEMORY, NULL, context);
+ callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
return NULL;
}
listener->schedule = schedule;
if (local_ip_count > 0) {
listener->socks = (SilcSocket *)silc_calloc(local_ip_count,
- sizeof(*listener->socks));
+ sizeof(*listener->socks));
if (!listener->socks) {
- callback(SILC_NET_NO_MEMORY, NULL, context);
+ callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
return NULL;
}
} else {
listener->socks = (SilcSocket *)silc_calloc(1, sizeof(*listener->socks));
if (!listener->socks) {
- callback(SILC_NET_NO_MEMORY, NULL, context);
+ callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
return NULL;
}
if (ret != KErrNone)
goto err;
+#ifdef SILC_THREADS
+ /* Make our socket shareable between threads */
+ l->ss.ShareAuto();
+#endif /* SILC_THREADS */
+
/* Set listener address */
- if (local_ip_addr) {
- server = TInetAddr(port);
- tmp = (TText *)local_ip_addr[i];
- ret = server.Input(tmp);
- if (ret != KErrNone)
- goto err;
- } else {
- server = TInetAddr(KInetAddrAny, port);
+ if (!silc_net_set_sockaddr(&server, local_ip_addr[i], port))
+ goto err;
+
+ /* Create the socket */
+ ret = l->sock.Open(l->ss, KAfInet, KSockStream, KProtocolInetTcp);
+ if (ret != KErrNone) {
+ SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
+ goto err;
+ }
+
+ /* Set the socket options */
+ ret = l->sock.SetOpt(KSoReuseAddr, KSolInetIp, 1);
+ if (ret != KErrNone) {
+ SILC_LOG_ERROR(("Cannot set socket options, error %d", ret));
+ goto err;
+ }
+
+ /* Bind the listener socket */
+ ret = l->sock.Bind(server);
+ if (ret != KErrNone) {
+ SILC_LOG_DEBUG(("Cannot bind socket, error %d", ret));
+ goto err;
+ }
+
+ /* Specify that we are listenning */
+ ret = l->sock.Listen(5);
+ if (ret != KErrNone) {
+ SILC_LOG_ERROR(("Cannot set socket listenning, error %d", ret));
+ goto err;
+ }
+ l->Listen();
+
+ l->listener = listener;
+ listener->socks[i] = (SilcSocket)l;
+ listener->socks_count++;
+ }
+
+ SILC_LOG_DEBUG(("TCP listener created"));
+
+ return listener;
+
+ err:
+ if (l)
+ delete l;
+ if (callback)
+ callback(SILC_ERR, NULL, context);
+ if (listener)
+ silc_net_close_listener(listener);
+ return NULL;
+}
+
+/* Create TCP listener, multiple ports */
+
+SilcNetListener
+silc_net_tcp_create_listener2(const char *local_ip_addr, int *ports,
+ SilcUInt32 port_count,
+ SilcBool ignore_port_error,
+ SilcBool lookup, SilcBool require_fqdn,
+ SilcSchedule schedule,
+ SilcNetCallback callback, void *context)
+{
+ SilcNetListener listener = NULL;
+ SilcSymbianTCPListener *l = NULL;
+ TInetAddr server;
+ TInt ret;
+ int i;
+
+ SILC_LOG_DEBUG(("Creating TCP listener"));
+
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ goto err;
+ }
+ }
+
+ if (!callback) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ goto err;
+ }
+
+ listener = (SilcNetListener)silc_calloc(1, sizeof(*listener));
+ if (!listener) {
+ callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
+ return NULL;
+ }
+ listener->schedule = schedule;
+ listener->callback = callback;
+ listener->context = context;
+ listener->require_fqdn = require_fqdn;
+ listener->lookup = lookup;
+
+ if (port_count > 0) {
+ listener->socks = (SilcSocket *)silc_calloc(port_count,
+ sizeof(*listener->socks));
+ if (!listener->socks) {
+ callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
+ return NULL;
+ }
+ } else {
+ listener->socks = (SilcSocket *)silc_calloc(1, sizeof(*listener->socks));
+ if (!listener->socks) {
+ callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
+ return NULL;
+ }
+
+ port_count = 1;
+ }
+
+ /* Bind to ports */
+ for (i = 0; i < port_count; i++) {
+ SILC_LOG_DEBUG(("Binding to local address %s:%d",
+ local_ip_addr ? local_ip_addr : "0.0.0.0",
+ ports ? ports[i] : 0));
+
+ l = new SilcSymbianTCPListener;
+ if (!l)
+ goto err;
+
+ /* Connect to socket server */
+ ret = l->ss.Connect();
+ if (ret != KErrNone)
+ goto err;
+
+#ifdef SILC_THREADS
+ /* Make our socket shareable between threads */
+ l->ss.ShareAuto();
+#endif /* SILC_THREADS */
+
+ /* Set listener address */
+ if (!silc_net_set_sockaddr(&server, local_ip_addr, ports ? ports[i] : 0)) {
+ if (ignore_port_error) {
+ delete l;
+ continue;
+ }
+ goto err;
}
/* Create the socket */
ret = l->sock.Open(l->ss, KAfInet, KSockStream, KProtocolInetTcp);
if (ret != KErrNone) {
- SILC_LOG_ERROR(("Cannot create socket"));
+ if (ignore_port_error) {
+ delete l;
+ continue;
+ }
+ SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
goto err;
}
/* Set the socket options */
ret = l->sock.SetOpt(KSoReuseAddr, KSolInetIp, 1);
if (ret != KErrNone) {
- SILC_LOG_ERROR(("Cannot set socket options"));
+ if (ignore_port_error) {
+ delete l;
+ continue;
+ }
+ SILC_LOG_ERROR(("Cannot set socket options, error %d", ret));
goto err;
}
/* Bind the listener socket */
ret = l->sock.Bind(server);
if (ret != KErrNone) {
- SILC_LOG_DEBUG(("Cannot bind socket"));
+ if (ignore_port_error) {
+ delete l;
+ continue;
+ }
+ SILC_LOG_DEBUG(("Cannot bind socket, error %d", ret));
goto err;
}
/* Specify that we are listenning */
ret = l->sock.Listen(5);
if (ret != KErrNone) {
- SILC_LOG_ERROR(("Cannot set socket listenning"));
+ if (ignore_port_error) {
+ delete l;
+ continue;
+ }
+ SILC_LOG_ERROR(("Cannot set socket listenning, error %d", ret));
goto err;
}
l->Listen();
listener->socks_count++;
}
+ if (ignore_port_error && !listener->socks_count) {
+ l = NULL;
+ goto err;
+ }
+
SILC_LOG_DEBUG(("TCP listener created"));
return listener;
if (l)
delete l;
if (callback)
- callback(SILC_NET_ERROR, NULL, context);
+ callback(SILC_ERR, NULL, context);
if (listener)
silc_net_close_listener(listener);
return NULL;
SILC_LOG_DEBUG(("Closing network listener"));
+ if (!listener)
+ return;
+
for (i = 0; i < listener->socks_count; i++) {
SilcSymbianTCPListener *l = (SilcSymbianTCPListener *)listener->socks[i];
l->sock.CancelAll();
silc_free(listener);
}
+
/**************************** TCP/IP connecting *****************************/
-static void silc_net_connect_stream(SilcSocketStreamStatus status,
+static void silc_net_connect_stream(SilcResult status,
SilcStream stream, void *context);
+} /* extern "C" */
+
/* TCP connecting class */
class SilcSymbianTCPConnect : public CActive {
/* Connect to remote host */
void Connect(TSockAddr &addr)
{
+ SILC_LOG_DEBUG(("Connect()"));
sock->Connect(addr, iStatus);
SetActive();
}
/* Connection callback */
virtual void RunL()
{
+ SILC_LOG_DEBUG(("RunL(), iStatus=%d", iStatus));
+
if (iStatus != KErrNone) {
if (callback)
- callback(SILC_NET_ERROR, NULL, context);
+ callback(SILC_ERR, NULL, context);
sock->CancelConnect();
delete sock;
ss->Close();
return;
}
+ SILC_LOG_DEBUG(("Connected to host %s on %d", remote_ip, port));
+
/* Create stream */
if (callback) {
silc_socket_tcp_stream_create(
(SilcSocket)silc_create_symbian_socket(sock, ss),
- FALSE, FALSE, schedule, silc_net_connect_stream,
+ TRUE, FALSE, schedule, silc_net_connect_stream,
(void *)this);
+ sock = NULL;
+ ss = NULL;
} else {
sock->Close();
delete sock;
delete ss;
sock = NULL;
ss = NULL;
+ delete this;
}
-
- delete this;
}
/* Cancel */
void *context;
};
-/* Stream creation callback */
+extern "C" {
+
+/* TCP stream creation callback */
-static void silc_net_connect_stream(SilcSocketStreamStatus status,
+static void silc_net_connect_stream(SilcResult status,
SilcStream stream, void *context)
{
SilcSymbianTCPConnect *conn = (SilcSymbianTCPConnect *)context;
- SilcNetStatus net_status = SILC_NET_OK;
-
- if (status != SILC_SOCKET_OK) {
- /* In case of error, the socket has been destroyed already */
- if (status == SILC_SOCKET_UNKNOWN_IP)
- net_status = SILC_NET_UNKNOWN_IP;
- else if (status == SILC_SOCKET_UNKNOWN_HOST)
- net_status = SILC_NET_UNKNOWN_HOST;
- else
- net_status = SILC_NET_ERROR;
- }
- /* Set stream information */
- if (stream && conn->callback)
- silc_socket_stream_set_info(stream,
- !silc_net_is_ip(conn->remote) ? conn->remote :
- conn->remote_ip, conn->remote_ip, conn->port);
+ SILC_LOG_DEBUG(("Socket stream creation status %d", status));
/* Call connection callback */
if (conn->callback)
- conn->callback(net_status, stream, conn->context);
+ conn->callback(status, stream, conn->context);
else if (stream)
silc_stream_destroy(stream);
conn->callback = NULL;
conn->op = NULL;
if (conn->sock)
- sock->CancelConnect();
+ conn->sock->CancelConnect();
}
/* Create TCP/IP connection */
{
SilcSymbianTCPConnect *conn;
TInetAddr local, remote;
- SilcNetStatus status;
- TBuf<64> tmp;
+ SilcResult status;
TInt ret;
- if (!remote_ip_addr || remote_port < 1 || !schedule || !callback)
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ return NULL;
+ }
+ }
+
+ if (!remote_ip_addr || remote_port < 1 || !callback) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
return NULL;
+ }
SILC_LOG_DEBUG(("Creating connection to host %s port %d",
remote_ip_addr, remote_port));
conn = new SilcSymbianTCPConnect;
if (!conn) {
- callback(SILC_NET_NO_MEMORY, NULL, context);
+ callback(SILC_ERR_OUT_OF_MEMORY, NULL, context);
return NULL;
}
conn->schedule = schedule;
conn->port = remote_port;
conn->remote = strdup(remote_ip_addr);
if (!conn->remote) {
- status = SILC_NET_NO_MEMORY;
+ status = SILC_ERR_OUT_OF_MEMORY;
goto err;
}
/* Allocate socket */
conn->sock = new RSocket;
if (!conn->sock) {
- status = SILC_NET_NO_MEMORY;
+ status = SILC_ERR_OUT_OF_MEMORY;
goto err;
}
/* Allocate socket server */
conn->ss = new RSocketServ;
if (!conn->ss) {
- status = SILC_NET_NO_MEMORY;
+ status = SILC_ERR_OUT_OF_MEMORY;
goto err;
}
/* Connect to socket server */
ret = conn->ss->Connect();
if (ret != KErrNone) {
- status = SILC_NET_ERROR;
+ SILC_LOG_ERROR(("Error connecting to socket server, error %d", ret));
+ status = SILC_ERR;
goto err;
}
+#ifdef SILC_THREADS
+ /* Make our socket shareable between threads */
+ conn->ss->ShareAuto();
+#endif /* SILC_THREADS */
+
/* Start async operation */
conn->op = silc_async_alloc(silc_net_connect_abort, NULL, (void *)conn);
if (!conn->op) {
- status = SILC_NET_NO_MEMORY;
+ status = SILC_ERR_OUT_OF_MEMORY;
goto err;
}
/* Do host lookup */
- if (!silc_net_is_ip(remote_ip_addr)) {
- if (!silc_net_gethostbyname(remote_ip_addr, FALSE, conn->remote_ip,
- sizeof(conn->remote_ip))) {
- SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
- "host", conn->remote));
- status = SILC_NET_HOST_UNREACHABLE;
- goto err;
- }
- } else {
- strcpy(conn->remote_ip, remote_ip_addr);
+ if (!silc_net_gethostbyname(remote_ip_addr, FALSE, conn->remote_ip,
+ sizeof(conn->remote_ip))) {
+ SILC_LOG_ERROR(("Network (%s) unreachable: could not resolve the "
+ "host", conn->remote));
+ status = SILC_ERR_UNREACHABLE;
+ goto err;
}
/* Create the connection socket */
ret = conn->sock->Open(*conn->ss, KAfInet, KSockStream, KProtocolInetTcp);
if (ret != KErrNone) {
- SILC_LOG_ERROR(("Cannot create socket"));
- status = SILC_NET_ERROR;
+ SILC_LOG_ERROR(("Cannot create socket, error %d", ret));
+ status = SILC_ERR;
goto err;
}
conn->sock->SetOpt(KSoTcpKeepAlive, KSolInetTcp, 1);
/* Bind to the local address if provided */
- if (local_ip_addr) {
- local = TInetAddr(0);
- tmp = (TText *)local_ip_addr;
- ret = local.Input(tmp);
- if (ret == KErrNone)
- ret = conn->sock->Bind(local);
- }
+ if (local_ip_addr)
+ if (silc_net_set_sockaddr(&local, local_ip_addr, 0))
+ conn->sock->Bind(local);
/* Connect to the host */
- remote = TInetAddr(remote_port);
- tmp = (TText *)conn->remote_ip;
- ret = remote.Input(tmp);
- if (ret != KErrNone) {
+ if (!silc_net_set_sockaddr(&remote, conn->remote_ip, remote_port)) {
SILC_LOG_ERROR(("Cannot connect (cannot set address)"));
- status = SILC_NET_ERROR;
+ status = SILC_ERR;
goto err;
}
conn->Connect(remote);
TRequestStatus status;
RSocket *sock = NULL;
RSocketServ *ss = NULL;
- TBuf<64> tmp;
TInt ret;
SILC_LOG_DEBUG(("Creating UDP stream"));
- if (!schedule)
- goto err;
+ if (!schedule) {
+ schedule = silc_schedule_get_global();
+ if (!schedule) {
+ silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
+ goto err;
+ }
+ }
SILC_LOG_DEBUG(("Binding to local address %s",
local_ip_addr ? local_ip_addr : "0.0.0.0"));
if (ret != KErrNone)
goto err;
+#ifdef SILC_THREADS
+ /* Make our socket shareable between threads */
+ ss->ShareAuto();
+#endif /* SILC_THREADS */
+
/* Get local bind address */
- if (local_ip_addr) {
- local = TInetAddr(local_port);
- tmp = (TText *)local_ip_addr;
- ret = local.Input(tmp);
- if (ret != KErrNone)
- goto err;
- } else {
- local = TInetAddr(KInetAddrAny, local_port);
- }
+ if (!silc_net_set_sockaddr(&local, local_ip_addr, local_port))
+ goto err;
/* Create the socket */
ret = sock->Open(*ss, KAfInet, KSockDatagram, KProtocolInetUdp);
/* Set to connected state if remote address is provided. */
if (remote_ip_addr && remote_port) {
- remote = TInetAddr(remote_port);
- tmp = (TText *)remote_ip_addr;
- ret = remote.Input(tmp);
- if (ret != KErrNone)
- goto err;
-
- sock->Connect(remote, status);
- if (status != KErrNone) {
- SILC_LOG_DEBUG(("Cannot connect UDP stream"));
- goto err;
+ if (silc_net_set_sockaddr(&remote, remote_ip_addr, remote_port)) {
+ sock->Connect(remote, status);
+ if (status != KErrNone) {
+ SILC_LOG_DEBUG(("Cannot connect UDP stream"));
+ goto err;
+ }
}
}
SilcBool silc_net_addr2bin(const char *addr, void *bin, SilcUInt32 bin_len)
{
int ret = 0;
-
struct in_addr tmp;
+
ret = inet_aton(addr, &tmp);
if (bin_len < 4)
return FALSE;
s->sock->LocalName(addr);
return (SilcUInt16)addr.Port();
}
+
+} /* extern "C" */