client.c
- Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+ Author: Pekka Riikonen <priikone@silcnet.org>
Copyright (C) 1997 - 2001 Pekka Riikonen
SILC_TASK_CALLBACK(silc_client_connect_to_server_start);
SILC_TASK_CALLBACK(silc_client_connect_to_server_second);
SILC_TASK_CALLBACK(silc_client_connect_to_server_final);
-SILC_TASK_CALLBACK(silc_client_packet_parse_real);
SILC_TASK_CALLBACK(silc_client_rekey_callback);
SILC_TASK_CALLBACK(silc_client_rekey_final);
-static void silc_client_packet_parse(SilcPacketParserContext *parser_context);
+static bool silc_client_packet_parse(SilcPacketParserContext *parser_context,
+ void *context);
static void silc_client_packet_parse_type(SilcClient client,
SilcSocketConnection sock,
SilcPacketContext *packet);
+void silc_client_resolve_auth_method(bool success,
+ SilcProtocolAuthMeth auth_meth,
+ const unsigned char *auth_data,
+ uint32 auth_data_len, void *context);
/* Allocates new client object. This has to be done before client may
work. After calling this one must call silc_client_init to initialize
if (params)
memcpy(new_client->params, params, sizeof(*params));
+ if (!new_client->params->task_max)
+ new_client->params->task_max = 200;
+
if (!new_client->params->rekey_secs)
new_client->params->rekey_secs = 3600;
+ if (!new_client->params->connauth_request_secs)
+ new_client->params->connauth_request_secs = 2;
+
+ new_client->params->
+ nickname_format[sizeof(new_client->params->nickname_format) - 1] = 0;
+
return new_client;
}
silc_schedule(client->schedule);
}
+static void silc_client_entry_destructor(SilcIDCache cache,
+ SilcIDCacheEntry entry)
+{
+ silc_free(entry->name);
+}
+
/* Allocates and adds new connection to the client. This adds the allocated
connection to the connection table and returns a pointer to it. A client
can have multiple connections to multiple servers. Every connection must
conn = silc_calloc(1, sizeof(*conn));
/* Initialize ID caches */
- conn->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT, NULL);
+ conn->client_cache = silc_idcache_alloc(0, SILC_ID_CLIENT,
+ silc_client_entry_destructor);
conn->channel_cache = silc_idcache_alloc(0, SILC_ID_CHANNEL, NULL);
conn->server_cache = silc_idcache_alloc(0, SILC_ID_SERVER, NULL);
conn->client = client;
conn->remote_port = port;
conn->context = context;
conn->pending_commands = silc_dlist_init();
+ conn->ftp_sessions = silc_dlist_init();
/* Add the connection to connections table */
for (i = 0; i < client->conns_count; i++)
for (i = 0; i < client->conns_count; i++)
if (client->conns[i] == conn) {
+
+ silc_idcache_free(conn->client_cache);
+ silc_idcache_free(conn->channel_cache);
+ silc_idcache_free(conn->server_cache);
if (conn->pending_commands)
silc_dlist_uninit(conn->pending_commands);
+ silc_free(conn->remote_host);
+ silc_dlist_uninit(conn->ftp_sessions);
silc_free(conn);
+
client->conns[i] = NULL;
}
}
/* XXX In the future we should give up this non-blocking connect all
together and use threads instead. */
/* Create connection to server asynchronously */
- sock = silc_net_create_connection_async(ctx->port, ctx->host);
+ sock = silc_net_create_connection_async(NULL, ctx->port, ctx->host);
if (sock < 0)
return -1;
used only if the application performed the connecting outside the library.
The library however may use this internally. */
-int silc_client_start_key_exchange(SilcClient client,
- SilcClientConnection conn,
- int fd)
+bool silc_client_start_key_exchange(SilcClient client,
+ SilcClientConnection conn,
+ int fd)
{
SilcProtocol protocol;
SilcClientKEInternalContext *proto_ctx;
silc_client_connect_to_server_second);
if (!protocol) {
client->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
- "Error: Could not start authentication protocol");
+ "Error: Could not start key exchange protocol");
return FALSE;
}
conn->sock->protocol = protocol;
ctx->ske->prop->pkcs,
ctx->ske->prop->hash,
ctx->ske->prop->hmac,
- ctx->ske->prop->group);
+ ctx->ske->prop->group,
+ ctx->responder);
silc_ske_free_key_material(ctx->keymat);
/* Allocate internal context for the authentication protocol. This
proto_ctx->dest_id_type = ctx->dest_id_type;
proto_ctx->dest_id = ctx->dest_id;
- /* Resolve the authentication method to be used in this connection */
- if (!client->ops->get_auth_method(client, sock->user_data, sock->hostname,
- sock->port, &proto_ctx->auth_meth,
- &proto_ctx->auth_data,
- &proto_ctx->auth_data_len))
- proto_ctx->auth_meth = SILC_AUTH_NONE;
-
/* Free old protocol as it is finished now */
silc_protocol_free(protocol);
if (ctx->packet)
silc_free(ctx);
sock->protocol = NULL;
+ /* Resolve the authentication method to be used in this connection. The
+ completion callback is called after the application has resolved
+ the authentication method. */
+ client->ops->get_auth_method(client, sock->user_data, sock->hostname,
+ sock->port, silc_client_resolve_auth_method,
+ proto_ctx);
+}
+
+/* Authentication method resolving callback. Application calls this function
+ after we've called the client->ops->get_auth_method client operation
+ to resolve the authentication method. We will continue the executiong
+ of the protocol in this function. */
+
+void silc_client_resolve_auth_method(bool success,
+ SilcProtocolAuthMeth auth_meth,
+ const unsigned char *auth_data,
+ uint32 auth_data_len, void *context)
+{
+ SilcClientConnAuthInternalContext *proto_ctx =
+ (SilcClientConnAuthInternalContext *)context;
+ SilcClient client = (SilcClient)proto_ctx->client;
+
+ if (!success)
+ auth_meth = SILC_AUTH_NONE;
+
+ proto_ctx->auth_meth = auth_meth;
+
+ if (auth_data && auth_data_len) {
+ proto_ctx->auth_data = silc_calloc(auth_data_len, sizeof(*auth_data));
+ memcpy(proto_ctx->auth_data, auth_data, auth_data_len);
+ proto_ctx->auth_data_len = auth_data_len;
+ }
+
/* Allocate the authenteication protocol and execute it. */
silc_protocol_alloc(SILC_PROTOCOL_CLIENT_CONNECTION_AUTH,
- &sock->protocol, (void *)proto_ctx,
+ &proto_ctx->sock->protocol, (void *)proto_ctx,
silc_client_connect_to_server_final);
/* Execute the protocol */
- silc_protocol_execute(sock->protocol, client->schedule, 0, 0);
+ silc_protocol_execute(proto_ctx->sock->protocol, client->schedule, 0, 0);
}
/* Finalizes the connection to the remote SILC server. This is called
conn->rekey->timeout = client->params->rekey_secs;
conn->rekey->context = (void *)client;
silc_schedule_task_add(client->schedule, conn->sock->sock,
- silc_client_rekey_callback,
- (void *)conn->sock, conn->rekey->timeout, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+ silc_client_rekey_callback,
+ (void *)conn->sock, conn->rekey->timeout, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
silc_protocol_free(protocol);
if (ctx->auth_data)
int silc_client_packet_send_real(SilcClient client,
SilcSocketConnection sock,
- bool force_send,
- bool flush)
+ bool force_send)
{
int ret;
/* If rekey protocol is active we must assure that all packets are
sent through packet queue. */
- if (flush == FALSE && SILC_CLIENT_IS_REKEY(sock))
+ if (SILC_CLIENT_IS_REKEY(sock))
+ force_send = FALSE;
+
+ /* If outbound data is already pending do not force send */
+ if (SILC_IS_OUTBUF_PENDING(sock))
force_send = FALSE;
/* Send the packet */
/* Packet sending */
if (type == SILC_TASK_WRITE) {
- SILC_LOG_DEBUG(("Writing data to connection"));
+ /* Do not send data to disconnected connection */
+ if (SILC_IS_DISCONNECTED(sock))
+ return;
if (sock->outbuf->data - sock->outbuf->head)
- silc_buffer_push(sock->outbuf,
- sock->outbuf->data - sock->outbuf->head);
+ silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
- ret = silc_client_packet_send_real(client, sock, TRUE, TRUE);
+ ret = silc_packet_send(sock, TRUE);
/* If returned -2 could not write to connection now, will do
it later. */
if (ret == -2)
return;
+
+ /* Error */
+ if (ret == -1)
+ return;
/* The packet has been sent and now it is time to set the connection
back to only for input. When there is again some outgoing data
/* Packet receiving */
if (type == SILC_TASK_READ) {
- SILC_LOG_DEBUG(("Reading data from connection"));
-
/* Read data from network */
ret = silc_packet_receive(sock);
if (ret < 0)
/* If connection is disconnecting already we will finally
close the connection */
if (SILC_IS_DISCONNECTING(sock)) {
- if (sock == conn->sock)
+ if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
client->ops->disconnect(client, conn);
silc_client_close_connection(client, sock, conn);
return;
}
SILC_LOG_DEBUG(("EOF from connection %d", sock->sock));
- if (sock == conn->sock)
+ if (sock == conn->sock && sock->type != SILC_SOCKET_TYPE_CLIENT)
client->ops->disconnect(client, conn);
silc_client_close_connection(client, sock, conn);
return;
/* Process the packet. This will call the parser that will then
decrypt and parse the packet. */
if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
- silc_packet_receive_process(sock, conn->receive_key, conn->hmac_receive,
+ silc_packet_receive_process(sock, FALSE, conn->receive_key,
+ conn->hmac_receive, conn->psn_receive,
silc_client_packet_parse, client);
else
- silc_packet_receive_process(sock, NULL, NULL,
+ silc_packet_receive_process(sock, FALSE, NULL, NULL, 0,
silc_client_packet_parse, client);
}
}
-/* Callback function that the silc_packet_decrypt will call to make the
- decision whether the packet is normal or special packet. We will
- return TRUE if it is normal and FALSE if it is special */
-
-static int silc_client_packet_decrypt_check(SilcPacketType packet_type,
- SilcBuffer buffer,
- SilcPacketContext *packet,
- void *context)
-{
-
- /* Packet is normal packet, if:
-
- 1) packet is private message packet and does not have private key set
- 2) is other packet than channel message packet
-
- all other packets are special packets
- */
-
- if (packet_type == SILC_PACKET_PRIVATE_MESSAGE &&
- (buffer->data[2] & SILC_PACKET_FLAG_PRIVMSG_KEY))
- return FALSE;
-
- if (packet_type != SILC_PACKET_CHANNEL_MESSAGE)
- return TRUE;
-
- return FALSE;
-}
-
-/* Parses whole packet, received earlier. */
+/* Parser callback called by silc_packet_receive_process. Thie merely
+ registers timeout that will handle the actual parsing when appropriate. */
-SILC_TASK_CALLBACK(silc_client_packet_parse_real)
+static bool silc_client_packet_parse(SilcPacketParserContext *parser_context,
+ void *context)
{
- SilcPacketParserContext *parse_ctx = (SilcPacketParserContext *)context;
- SilcClient client = (SilcClient)parse_ctx->context;
- SilcPacketContext *packet = parse_ctx->packet;
- SilcBuffer buffer = packet->buffer;
- SilcSocketConnection sock = parse_ctx->sock;
+ SilcClient client = (SilcClient)context;
+ SilcSocketConnection sock = parser_context->sock;
SilcClientConnection conn = (SilcClientConnection)sock->user_data;
- int ret;
+ SilcPacketContext *packet = parser_context->packet;
+ SilcPacketType ret;
- SILC_LOG_DEBUG(("Start"));
+ if (conn && conn->hmac_receive && conn->sock == sock)
+ conn->psn_receive = parser_context->packet->sequence + 1;
- /* Decrypt the received packet */
- if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
- ret = silc_packet_decrypt(conn->receive_key, conn->hmac_receive,
- buffer, packet,
- silc_client_packet_decrypt_check, parse_ctx);
+ /* Parse the packet immediately */
+ if (parser_context->normal)
+ ret = silc_packet_parse(packet, conn->receive_key);
else
- ret = silc_packet_decrypt(NULL, NULL, buffer, packet,
- silc_client_packet_decrypt_check, parse_ctx);
+ ret = silc_packet_parse_special(packet, conn->receive_key);
- if (ret < 0)
- goto out;
-
- if (ret == 0) {
- /* Parse the packet. Packet type is returned. */
- ret = silc_packet_parse(packet);
- } else {
- /* Parse the packet header in special way as this is "special"
- packet type. */
- ret = silc_packet_parse_special(packet);
+ if (ret == SILC_PACKET_NONE) {
+ silc_packet_context_free(packet);
+ silc_free(parser_context);
+ return FALSE;
+ }
+
+ /* If protocol for this connection is key exchange or rekey then we'll
+ process all packets synchronously, since there might be packets in
+ queue that we are not able to decrypt without first processing the
+ packets before them. */
+ if ((ret == SILC_PACKET_REKEY || ret == SILC_PACKET_REKEY_DONE) ||
+ (sock->protocol && sock->protocol->protocol &&
+ (sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE ||
+ sock->protocol->protocol->type == SILC_PROTOCOL_CLIENT_REKEY))) {
+
+ /* Parse the incoming packet type */
+ silc_client_packet_parse_type(client, sock, packet);
+ silc_packet_context_free(packet);
+ silc_free(parser_context);
+
+ /* Reprocess the buffer since we'll return FALSE. This is because
+ the `conn->receive_key' might have become valid by processing
+ the previous packet */
+ if (sock->type != SILC_SOCKET_TYPE_UNKNOWN)
+ silc_packet_receive_process(sock, FALSE, conn->receive_key,
+ conn->hmac_receive, conn->psn_receive,
+ silc_client_packet_parse, client);
+ else
+ silc_packet_receive_process(sock, FALSE, NULL, NULL, 0,
+ silc_client_packet_parse, client);
+
+ return FALSE;
}
-
- if (ret == SILC_PACKET_NONE)
- goto out;
/* Parse the incoming packet type */
silc_client_packet_parse_type(client, sock, packet);
-
- out:
- /* silc_buffer_clear(sock->inbuf); */
silc_packet_context_free(packet);
- silc_free(parse_ctx);
-}
-
-/* Parser callback called by silc_packet_receive_process. Thie merely
- registers timeout that will handle the actual parsing when appropriate. */
-
-void silc_client_packet_parse(SilcPacketParserContext *parser_context)
-{
- SilcClient client = (SilcClient)parser_context->context;
-
- /* Parse the packet */
- silc_schedule_task_add(client->schedule, parser_context->sock->sock,
- silc_client_packet_parse_real,
- (void *)parser_context, 0, 1,
- SILC_TASK_TIMEOUT,
- SILC_TASK_PRI_NORMAL);
+ silc_free(parser_context);
+ return TRUE;
}
/* Parses the packet type and calls what ever routines the packet type
}
break;
+ case SILC_PACKET_CONNECTION_AUTH_REQUEST:
+ /*
+ * Reveived reply to our connection authentication method request
+ * packet. This is used to resolve the authentication method for the
+ * current session from the server if the client does not know it.
+ */
+ silc_client_connection_auth_request(client, sock, packet);
+ break;
+
+ case SILC_PACKET_FTP:
+ /* Received file transfer packet. */
+ silc_client_ftp(client, sock, packet);
+ break;
+
default:
SILC_LOG_DEBUG(("Incorrect packet type %d, packet dropped", type));
break;
int force_send)
{
SilcPacketContext packetdata;
+ int block_len;
+ uint32 sequence = 0;
if (!sock)
return;
dst_id = ((SilcClientConnection)sock->user_data)->remote_id;
dst_id_type = SILC_ID_SERVER;
}
+
+ if (hmac)
+ sequence = ((SilcClientConnection)sock->user_data)->psn_send++;
}
+ block_len = cipher ? silc_cipher_get_block_len(cipher) : 0;
+
/* Set the packet context pointers */
packetdata.flags = 0;
packetdata.type = type;
}
packetdata.truelen = data_len + SILC_PACKET_HEADER_LEN +
packetdata.src_id_len + packetdata.dst_id_len;
- packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen);
+ packetdata.padlen = SILC_PACKET_PADLEN(packetdata.truelen, block_len);
/* Prepare outgoing data buffer for packet sending */
silc_packet_send_prepare(sock,
silc_buffer_put(sock->outbuf, data, data_len);
/* Create the outgoing packet */
- silc_packet_assemble(&packetdata);
+ silc_packet_assemble(&packetdata, cipher);
/* Encrypt the packet */
if (cipher)
- silc_packet_encrypt(cipher, hmac, sock->outbuf, sock->outbuf->len);
+ silc_packet_encrypt(cipher, hmac, sequence, sock->outbuf,
+ sock->outbuf->len);
- SILC_LOG_HEXDUMP(("Packet, len %d", sock->outbuf->len),
+ SILC_LOG_HEXDUMP(("Packet (%d), len %d", sequence, sock->outbuf->len),
sock->outbuf->data, sock->outbuf->len);
/* Now actually send the packet */
- silc_client_packet_send_real(client, sock, force_send, FALSE);
+ silc_client_packet_send_real(client, sock, force_send);
+}
+
+void silc_client_packet_queue_purge(SilcClient client,
+ SilcSocketConnection sock)
+{
+ if (sock && SILC_IS_OUTBUF_PENDING(sock) &&
+ (SILC_IS_DISCONNECTED(sock) == FALSE)) {
+ if (sock->outbuf->data - sock->outbuf->head)
+ silc_buffer_push(sock->outbuf, sock->outbuf->data - sock->outbuf->head);
+
+ silc_packet_send(sock, TRUE);
+
+ SILC_CLIENT_SET_CONNECTION_FOR_INPUT(client->schedule, sock->sock);
+ SILC_UNSET_OUTBUF_PENDING(sock);
+ silc_buffer_clear(sock->outbuf);
+ }
}
/* Closes connection to remote end. Free's all allocated data except
{
int del = FALSE;
+ SILC_LOG_DEBUG(("Start"));
+
if (!sock || (sock && conn->sock == sock))
del = TRUE;
if (!sock)
silc_cipher_free(conn->send_key);
if (conn->receive_key)
silc_cipher_free(conn->receive_key);
- if (conn->hmac_send) /* conn->hmac_receive is same */
+ if (conn->hmac_send)
silc_hmac_free(conn->hmac_send);
+ if (conn->hmac_receive)
+ silc_hmac_free(conn->hmac_receive);
if (conn->pending_commands)
silc_dlist_uninit(conn->pending_commands);
if (conn->rekey)
silc_free(conn->rekey);
+ if (conn->active_session) {
+ sock->user_data = NULL;
+ silc_client_ftp_session_free(conn->active_session);
+ conn->active_session = NULL;
+ }
+
+ silc_client_ftp_free_sessions(client, conn);
+
memset(conn, 0, sizeof(*conn));
silc_client_del_connection(client, conn);
}
silc_socket_free(sock);
}
+/* Called when we receive disconnection packet from server. This
+ closes our end properly and displays the reason of the disconnection
+ on the screen. */
+
+SILC_TASK_CALLBACK(silc_client_disconnected_by_server_later)
+{
+ SilcClient client = (SilcClient)context;
+ SilcSocketConnection sock;
+
+ SILC_CLIENT_GET_SOCK(client, fd, sock);
+ if (sock == NULL)
+ return;
+
+ silc_client_close_connection(client, sock, sock->user_data);
+}
+
/* Called when we receive disconnection packet from server. This
closes our end properly and displays the reason of the disconnection
on the screen. */
silc_free(msg);
SILC_SET_DISCONNECTED(sock);
- silc_client_close_connection(client, sock, sock->user_data);
+
+ /* Close connection through scheduler. */
+ silc_schedule_task_add(client->schedule, sock->sock,
+ silc_client_disconnected_by_server_later,
+ client, 0, 1, SILC_TASK_TIMEOUT,
+ SILC_TASK_PRI_NORMAL);
}
/* Received error message from server. Display it on the screen.
conn->local_entry = silc_calloc(1, sizeof(*conn->local_entry));
conn->local_entry->nickname = conn->nickname;
- if (!conn->local_entry->username) {
- conn->local_entry->username =
- silc_calloc(strlen(client->username) + strlen(client->hostname) + 1,
- sizeof(conn->local_entry->username));
- sprintf(conn->local_entry->username, "%s@%s", client->username,
- client->hostname);
- }
+ if (!conn->local_entry->username)
+ conn->local_entry->username = strdup(client->username);
+ if (!conn->local_entry->hostname)
+ conn->local_entry->hostname = strdup(client->hostname);
conn->local_entry->server = strdup(conn->remote_host);
conn->local_entry->id = conn->local_id;
+ conn->local_entry->valid = TRUE;
/* Put it to the ID cache */
- silc_idcache_add(conn->client_cache, conn->nickname, conn->local_id,
+ silc_idcache_add(conn->client_cache, strdup(conn->nickname), conn->local_id,
(void *)conn->local_entry, FALSE);
/* Issue INFO command to fetch the real server name and server information
/* Re-register re-key timeout */
silc_schedule_task_add(client->schedule, sock->sock,
- silc_client_rekey_callback,
- context, conn->rekey->timeout, 0,
- SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+ silc_client_rekey_callback,
+ context, conn->rekey->timeout, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
}
/* The final callback for the REKEY protocol. This will actually take the
return;
}
+ /* Purge the outgoing data queue to assure that all rekey packets really
+ go to the network before we quit the protocol. */
+ silc_client_packet_queue_purge(client, sock);
+
/* Cleanup */
silc_protocol_free(protocol);
sock->protocol = NULL;
silc_socket_free(ctx->sock);
silc_free(ctx);
}
+
+/* Processes incoming connection authentication method request packet.
+ It is a reply to our previously sent request. The packet can be used
+ to resolve the authentication method for the current session if the
+ client does not know it beforehand. */
+
+void silc_client_connection_auth_request(SilcClient client,
+ SilcSocketConnection sock,
+ SilcPacketContext *packet)
+{
+ SilcClientConnection conn = (SilcClientConnection)sock->user_data;
+ uint16 conn_type, auth_meth;
+ int ret;
+
+ /* If we haven't send our request then ignore this one. */
+ if (!conn->connauth)
+ return;
+
+ /* Parse the payload */
+ ret = silc_buffer_unformat(packet->buffer,
+ SILC_STR_UI_SHORT(&conn_type),
+ SILC_STR_UI_SHORT(&auth_meth),
+ SILC_STR_END);
+ if (ret == -1)
+ auth_meth = SILC_AUTH_NONE;
+
+ /* Call the request callback to notify application for received
+ authentication method information. */
+ if (conn->connauth->callback)
+ (*conn->connauth->callback)(client, conn, auth_meth,
+ conn->connauth->context);
+
+ silc_schedule_task_del(client->schedule, conn->connauth->timeout);
+
+ silc_free(conn->connauth);
+ conn->connauth = NULL;
+}
+
+/* Timeout task callback called if the server does not reply to our
+ connection authentication method request in the specified time interval. */
+
+SILC_TASK_CALLBACK(silc_client_request_authentication_method_timeout)
+{
+ SilcClientConnection conn = (SilcClientConnection)context;
+ SilcClient client = conn->client;
+
+ if (!conn->connauth)
+ return;
+
+ /* Call the request callback to notify application */
+ if (conn->connauth->callback)
+ (*conn->connauth->callback)(client, conn, SILC_AUTH_NONE,
+ conn->connauth->context);
+
+ silc_free(conn->connauth);
+ conn->connauth = NULL;
+}
+
+/* This function can be used to request the current authentication method
+ from the server. This may be called when connecting to the server
+ and the client library requests the authentication data from the
+ application. If the application does not know the current authentication
+ method it can request it from the server using this function.
+ The `callback' with `context' will be called after the server has
+ replied back with the current authentication method. */
+
+void
+silc_client_request_authentication_method(SilcClient client,
+ SilcClientConnection conn,
+ SilcConnectionAuthRequest callback,
+ void *context)
+{
+ SilcClientConnAuthRequest connauth;
+ SilcBuffer packet;
+
+ connauth = silc_calloc(1, sizeof(*connauth));
+ connauth->callback = callback;
+ connauth->context = context;
+
+ if (conn->connauth)
+ silc_free(conn->connauth);
+
+ conn->connauth = connauth;
+
+ /* Assemble the request packet and send it to the server */
+ packet = silc_buffer_alloc(4);
+ silc_buffer_pull_tail(packet, SILC_BUFFER_END(packet));
+ silc_buffer_format(packet,
+ SILC_STR_UI_SHORT(SILC_SOCKET_TYPE_CLIENT),
+ SILC_STR_UI_SHORT(SILC_AUTH_NONE),
+ SILC_STR_END);
+ silc_client_packet_send(client, conn->sock,
+ SILC_PACKET_CONNECTION_AUTH_REQUEST,
+ NULL, 0, NULL, NULL,
+ packet->data, packet->len, FALSE);
+ silc_buffer_free(packet);
+
+ /* Register a timeout in case server does not reply anything back. */
+ connauth->timeout =
+ silc_schedule_task_add(client->schedule, conn->sock->sock,
+ silc_client_request_authentication_method_timeout,
+ conn, client->params->connauth_request_secs, 0,
+ SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
+}