Exemple #1
0
SilcClientFileError silc_client_file_close(SilcClient client,
					   SilcClientConnection conn,
					   SilcUInt32 session_id)
{
  SilcClientFtpSession session;

  if (!client || !conn)
    return SILC_CLIENT_FILE_ERROR;

  SILC_LOG_DEBUG(("Closing file transer session %d", session_id));

  /* Get the session */
  silc_dlist_start(client->internal->ftp_sessions);
  while ((session = silc_dlist_get(client->internal->ftp_sessions))
	 != SILC_LIST_END) {
    if (session->session_id == session_id)
      break;
  }

  if (session == SILC_LIST_END) {
    SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
    return SILC_CLIENT_FILE_UNKNOWN_SESSION;
  }

  if (session->monitor) {
    (*session->monitor)(session->client, session->conn,
			SILC_CLIENT_FILE_MONITOR_CLOSED,
			SILC_CLIENT_FILE_OK, 0, 0,
			session->client_entry, session->session_id,
			session->filepath, session->monitor_context);

    /* No more callbacks to application */
    session->monitor = NULL;
  }

  silc_schedule_task_del_by_context(client->schedule, session);

  session->closed = TRUE;

  /* Destroy via timeout */
  silc_schedule_task_add_timeout(conn->internal->schedule,
				 silc_client_file_close_final, session,
				 0, 1);

  return SILC_CLIENT_FILE_OK;
}
Exemple #2
0
SilcClientFileError
silc_client_file_receive(SilcClient client,
			 SilcClientConnection conn,
			 SilcClientConnectionParams *params,
			 SilcPublicKey public_key,
			 SilcPrivateKey private_key,
			 SilcClientFileMonitor monitor,
			 void *monitor_context,
			 const char *path,
			 SilcUInt32 session_id,
			 SilcClientFileAskName ask_name,
			 void *ask_name_context)
{
  SilcClientFtpSession session;
  SilcBuffer keyagr;

  if (!client || !conn)
    return SILC_CLIENT_FILE_ERROR;

  SILC_LOG_DEBUG(("Start, Session ID: %d", session_id));

  /* Get the session */
  silc_dlist_start(client->internal->ftp_sessions);
  while ((session = silc_dlist_get(client->internal->ftp_sessions))
	 != SILC_LIST_END) {
    if (session->session_id == session_id) {
      break;
    }
  }

  if (session == SILC_LIST_END) {
    SILC_LOG_DEBUG(("Unknown session ID: %d\n", session_id));
    return SILC_CLIENT_FILE_UNKNOWN_SESSION;
  }

  /* See if we have this session running already */
  if (session->sftp || session->listener) {
    SILC_LOG_DEBUG(("Session already started"));
    return SILC_CLIENT_FILE_ALREADY_STARTED;
  }

  session->monitor = monitor;
  session->monitor_context = monitor_context;
  session->ask_name = ask_name;
  session->ask_name_context = ask_name_context;
  session->path = path ? strdup(path) : NULL;

  /* If the hostname and port already exists then the remote client did
     provide the connection point to us and we won't create listener, but
     create the connection ourselves. */
  if (session->hostname && session->port) {
    SILC_LOG_DEBUG(("Connecting to remote client"));
    /* Connect to the remote client.  Performs key exchange automatically. */
    session->op =
      silc_client_connect_to_client(client, params, public_key, private_key,
				    session->hostname, session->port,
				    silc_client_ftp_connect_completion,
				    session);
    if (!session->op) {
      silc_free(session);
      return SILC_CLIENT_FILE_ERROR;
    }
  } else {
    /* Add the listener for the key agreement */
    SILC_LOG_DEBUG(("Creating listener for file transfer"));
    if (!params || (!params->local_ip && !params->bind_ip)) {
      session->client->internal->ops->say(session->client, session->conn,
					  SILC_CLIENT_MESSAGE_ERROR,
					  "Cannot create listener for file "
					  "transfer; IP address and/or port "
					  "not provided");
      silc_free(session);
      return SILC_CLIENT_FILE_ERROR;
    }
    session->listener =
      silc_client_listener_add(client, conn->internal->schedule, params,
			       public_key, private_key,
			       silc_client_ftp_connect_completion,
			       session);
    if (!session->listener) {
      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
				 "Cannot create listener for file transfer: "
				 "%s", strerror(errno));
      silc_free(session);
      return SILC_CLIENT_FILE_NO_MEMORY;
    }
    session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
			 strdup(params->local_ip));
    session->port = silc_client_listener_get_local_port(session->listener);

    /* Send the key agreement inside FTP packet */
    SILC_LOG_DEBUG(("Sending key agreement for file transfer"));
    keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
					       session->port);
    if (!keyagr) {
      silc_client_listener_free(session->listener);
      silc_free(session);
      return SILC_CLIENT_FILE_NO_MEMORY;
    }
    silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
			    SILC_ID_CLIENT, &session->client_entry->id,
			    NULL, NULL,
			    SILC_STR_UI_CHAR(1),
			    SILC_STR_DATA(silc_buffer_data(keyagr),
					  silc_buffer_len(keyagr)),
			    SILC_STR_END);
    silc_buffer_free(keyagr);

    /* Add session request timeout */
    if (params && params->timeout_secs)
      silc_schedule_task_add_timeout(client->schedule,
				     silc_client_ftp_timeout, session,
				     params->timeout_secs, 0);
  }

  return SILC_CLIENT_FILE_OK;
}
Exemple #3
0
SilcClientFileError
silc_client_file_send(SilcClient client,
		      SilcClientConnection conn,
		      SilcClientEntry client_entry,
		      SilcClientConnectionParams *params,
		      SilcPublicKey public_key,
		      SilcPrivateKey private_key,
		      SilcClientFileMonitor monitor,
		      void *monitor_context,
		      const char *filepath,
		      SilcUInt32 *session_id)
{
  SilcClientFtpSession session;
  SilcBuffer keyagr;
  char *filename, *path;
  int fd;

  SILC_LOG_DEBUG(("File send request (file: %s)", filepath));

  if (!client || !client_entry || !filepath || !params ||
      !public_key || !private_key)
    return SILC_CLIENT_FILE_ERROR;

  /* Check for existing session for `filepath'. */
  silc_dlist_start(client->internal->ftp_sessions);
  while ((session = silc_dlist_get(client->internal->ftp_sessions))) {
    if (session->filepath && !strcmp(session->filepath, filepath) &&
	session->client_entry == client_entry)
      return SILC_CLIENT_FILE_ALREADY_STARTED;
  }

  /* See whether the file exists and can be opened */
  fd = silc_file_open(filepath, O_RDONLY);
  if (fd < 0)
    return SILC_CLIENT_FILE_NO_SUCH_FILE;
  silc_file_close(fd);

  /* Add new session */
  session = silc_calloc(1, sizeof(*session));
  if (!session)
    return SILC_CLIENT_FILE_ERROR;
  session->session_id = ++client->internal->next_session_id;
  session->client = client;
  session->server_conn = conn;
  session->initiator = TRUE;
  session->client_entry = silc_client_ref_client(client, conn, client_entry);
  session->monitor = monitor;
  session->monitor_context = monitor_context;
  session->filepath = strdup(filepath);
  session->params = *params;
  session->public_key = public_key;
  session->private_key = private_key;

  if (silc_asprintf(&path, "file://%s", filepath) < 0) {
    silc_free(session);
    return SILC_CLIENT_FILE_NO_MEMORY;
  }

  /* Allocate memory filesystem and put the file to it */
  if (strrchr(path, '/'))
    filename = strrchr(path, '/') + 1;
  else
    filename = (char *)path;
  session->fs = silc_sftp_fs_memory_alloc(SILC_SFTP_FS_PERM_READ |
					  SILC_SFTP_FS_PERM_EXEC);
  silc_sftp_fs_memory_add_file(session->fs, NULL, SILC_SFTP_FS_PERM_READ,
			       filename, path);

  session->filesize = silc_file_size(filepath);

  /* If local IP is provided, create listener for incoming key exchange */
  if (params->local_ip || params->bind_ip) {
    session->listener =
      silc_client_listener_add(client,
			       conn->internal->schedule,
			       params, public_key, private_key,
			       silc_client_ftp_connect_completion,
			       session);
    if (!session->listener) {
      client->internal->ops->say(client, conn, SILC_CLIENT_MESSAGE_ERROR,
				 "Cannot create listener for file transfer: "
				 "%s", strerror(errno));
      silc_free(session);
      return SILC_CLIENT_FILE_NO_MEMORY;
    }

    session->hostname = (params->bind_ip ? strdup(params->bind_ip) :
			 strdup(params->local_ip));
    session->port = silc_client_listener_get_local_port(session->listener);
  }

  SILC_LOG_DEBUG(("Sending key agreement for file transfer"));

  /* Send the key agreement inside FTP packet */
  keyagr = silc_key_agreement_payload_encode(session->hostname, 0,
					     session->port);
  if (!keyagr) {
    if (session->listener)
      silc_client_listener_free(session->listener);
    silc_free(session);
    return SILC_CLIENT_FILE_NO_MEMORY;
  }
  silc_packet_send_va_ext(conn->stream, SILC_PACKET_FTP, 0, 0, NULL,
			  SILC_ID_CLIENT, &client_entry->id, NULL, NULL,
			  SILC_STR_UI_CHAR(1),
			  SILC_STR_DATA(silc_buffer_data(keyagr),
					silc_buffer_len(keyagr)),
			  SILC_STR_END);

  silc_buffer_free(keyagr);
  silc_free(path);

  silc_dlist_add(client->internal->ftp_sessions, session);
  if (session_id)
    *session_id = session->session_id;

  /* Add session request timeout */
  if (params && params->timeout_secs)
    silc_schedule_task_add_timeout(client->schedule,
				   silc_client_ftp_timeout, session,
				   params->timeout_secs, 0);

  return SILC_CLIENT_FILE_OK;
}
int silc_socket_stream_read(SilcStream stream, unsigned char *buf,
			    SilcUInt32 buf_len)
{
  SilcSocketStream sock = stream;
  int len = 0;
  struct timeval curtime;
  unsigned char *qosbuf;

  SILC_LOG_DEBUG(("Reading data from socket %d", sock->sock));

  /* Handle the simple non-QoS reading. */
  if (!sock->qos) {
    len = read(sock->sock, buf, buf_len);
    if (len < 0) {
      if (errno == EAGAIN || errno == EINTR) {
	SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
	silc_schedule_set_listen_fd(sock->schedule, sock->sock,
				    silc_schedule_get_fd_events(sock->schedule,
								sock->sock) |
				    SILC_TASK_READ, FALSE);
	return -1;
      }
      SILC_LOG_DEBUG(("Cannot read from socket: %d:%s",
		      sock->sock, strerror(errno)));
      silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
      sock->sock_error = errno;
      return -2;
    }

    SILC_LOG_DEBUG(("Read %d bytes", len));

    if (!len)
      silc_schedule_unset_listen_fd(sock->schedule, sock->sock);

    return len;
  }

  /* We have QoS set, and reading is done via the QoS system. */
  qosbuf = sock->qos->buffer;

  /* If QoS was applied, return the data that was pending. */
  if (sock->qos->applied && sock->qos->data_len) {
    memcpy(buf, qosbuf, sock->qos->data_len);
    len = sock->qos->data_len;
    sock->qos->data_len = 0;
    return len;
  }

  /* If we have active QoS data pending, return with no data */
  if (sock->qos->data_len) {
    silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
    return -1;
  }

  /* Read the data from the socket.  The qosbuf size is always the max
     read limit size. */
  len = (buf_len < sock->qos->read_limit_bytes ? buf_len :
	 sock->qos->read_limit_bytes);
  len = read(sock->sock, qosbuf, len);
  if (len < 0) {
    if (errno == EAGAIN || errno == EINTR) {
      SILC_LOG_DEBUG(("Could not read immediately, will do it later"));
      silc_schedule_set_listen_fd(sock->schedule, sock->sock,
				  silc_schedule_get_fd_events(sock->schedule,
							      sock->sock) |
				  SILC_TASK_READ, FALSE);
      return -1;
    }
    SILC_LOG_DEBUG(("Cannot read from socket: %d:%s",
		    sock->sock, strerror(errno)));
    silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
    silc_schedule_task_del_by_context(sock->schedule, sock->qos);
    sock->qos->data_len = 0;
    sock->sock_error = errno;
    return -2;
  }

  SILC_LOG_DEBUG(("Read %d bytes", len));

  if (!len) {
    silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
    silc_schedule_task_del_by_context(sock->schedule, sock->qos);
    sock->qos->data_len = 0;
    return 0;
  }

  /* If we have passed the rate time limit, set our new time limit,
     and zero the rate limit.  This limits reads per second. */
  silc_gettimeofday(&curtime);
  if (silc_compare_timeval(&curtime, &sock->qos->next_limit) > 0) {
    curtime.tv_sec++;
    sock->qos->next_limit = curtime;
    sock->qos->cur_rate = 0;
  }
  sock->qos->cur_rate++;

  /* If we are not within rate limit apply QoS for the read data */
  if (sock->qos->cur_rate > sock->qos->read_rate) {
    silc_schedule_task_add_timeout(sock->schedule, silc_socket_read_qos,
				   sock->qos, sock->qos->limit_sec,
				   sock->qos->limit_usec);
    sock->qos->data_len = len;

    /* Rate limit kicked in, do not return data yet */
    silc_schedule_unset_listen_fd(sock->schedule, sock->sock);
    return -1;
  }

  /* Return the data from the QoS buffer */
  memcpy(buf, qosbuf, len);
  return len;
}
Exemple #5
0
SilcBool silc_client_save_channel_key(SilcClient client,
				      SilcClientConnection conn,
				      SilcBuffer key_payload,
				      SilcChannelEntry channel)
{
  unsigned char *id_string, *key, *cipher, *hmac, hash[SILC_HASH_MAXLEN];
  SilcUInt32 tmp_len;
  SilcChannelID id;
  SilcChannelKeyPayload payload;

  SILC_LOG_DEBUG(("New channel key"));

  payload = silc_channel_key_payload_parse(silc_buffer_data(key_payload),
					   silc_buffer_len(key_payload));
  if (!payload)
    return FALSE;

  id_string = silc_channel_key_get_id(payload, &tmp_len);
  if (!id_string) {
    silc_channel_key_payload_free(payload);
    return FALSE;
  }

  if (!silc_id_str2id(id_string, tmp_len, SILC_ID_CHANNEL, &id, sizeof(id))) {
    silc_channel_key_payload_free(payload);
    return FALSE;
  }

  /* Find channel. */
  if (!channel) {
    channel = silc_client_get_channel_by_id(client, conn, &id);
    if (!channel) {
      SILC_LOG_DEBUG(("Key for unknown channel"));
      silc_channel_key_payload_free(payload);
      return FALSE;
    }
  } else {
    silc_client_ref_channel(client, conn, channel);
  }

  /* Save the old key for a short period of time so that we can decrypt
     channel message even after the rekey if some client would be sending
     messages with the old key after the rekey. */
  if (!channel->internal.old_channel_keys)
    channel->internal.old_channel_keys = silc_dlist_init();
  if (!channel->internal.old_hmacs)
    channel->internal.old_hmacs = silc_dlist_init();
  if (channel->internal.old_channel_keys && channel->internal.old_hmacs) {
    silc_dlist_add(channel->internal.old_channel_keys,
		   channel->internal.receive_key);
    silc_dlist_add(channel->internal.old_hmacs, channel->internal.hmac);
    silc_schedule_task_add_timeout(client->schedule,
				   silc_client_save_channel_key_rekey,
				   channel, 15, 0);
  }

  /* Get channel cipher */
  cipher = silc_channel_key_get_cipher(payload, NULL);
  if (!silc_cipher_alloc(cipher, &channel->internal.send_key)) {
    client->internal->ops->say(
			   conn->client, conn,
			   SILC_CLIENT_MESSAGE_AUDIT,
			   "Cannot talk to channel: unsupported cipher %s",
			   cipher);
    silc_client_unref_channel(client, conn, channel);
    silc_channel_key_payload_free(payload);
    return FALSE;
  }
  if (!silc_cipher_alloc(cipher, &channel->internal.receive_key)) {
    client->internal->ops->say(
			   conn->client, conn,
			   SILC_CLIENT_MESSAGE_AUDIT,
			   "Cannot talk to channel: unsupported cipher %s",
			   cipher);
    silc_client_unref_channel(client, conn, channel);
    silc_channel_key_payload_free(payload);
    return FALSE;
  }

  /* Set the cipher key.  Both sending and receiving keys are same */
  key = silc_channel_key_get_key(payload, &tmp_len);
  silc_cipher_set_key(channel->internal.send_key, key, tmp_len * 8, TRUE);
  silc_cipher_set_key(channel->internal.receive_key, key, tmp_len * 8, FALSE);

  /* Get channel HMAC */
  hmac = (channel->internal.hmac ?
	  (char *)silc_hmac_get_name(channel->internal.hmac) :
	  SILC_DEFAULT_HMAC);
  if (!silc_hmac_alloc(hmac, NULL, &channel->internal.hmac)) {
    client->internal->ops->say(
			   conn->client, conn,
			   SILC_CLIENT_MESSAGE_AUDIT,
			   "Cannot talk to channel: unsupported HMAC %s",
			   hmac);
    silc_client_unref_channel(client, conn, channel);
    silc_channel_key_payload_free(payload);
    return FALSE;
  }

  channel->cipher = silc_cipher_get_name(channel->internal.send_key);
  channel->hmac = silc_hmac_get_name(channel->internal.hmac);

  /* Set HMAC key */
  silc_hash_make(silc_hmac_get_hash(channel->internal.hmac), key,
		 tmp_len, hash);
  silc_hmac_set_key(channel->internal.hmac, hash,
		    silc_hash_len(silc_hmac_get_hash(channel->internal.hmac)));
  memset(hash, 0, sizeof(hash));
  silc_channel_key_payload_free(payload);

  silc_client_unref_channel(client, conn, channel);

  return TRUE;
}
Exemple #6
0
static void *silc_thread_pool_run_thread(void *context)
{
  SilcThreadPoolThread t = context, o, q;
  SilcThreadPool tp = t->tp;
  SilcMutex lock = t->lock;
  SilcCond thread_signal = t->thread_signal;

  silc_mutex_lock(lock);

  while (1) {
    /* Wait here for code to execute */
    while (!t->run && !t->stop)
      silc_cond_wait(thread_signal, lock);

    if (t->stop)
      goto stop;

    /* Execute code */
    silc_mutex_unlock(lock);
  execute:
    SILC_LOG_DEBUG(("Execute call %p, context %p, thread %p", t->run,
		    t->run_context, t));
    t->run(t->schedule, t->run_context);

    /* If scheduler is NULL, call completion directly from here.  Otherwise
       it is called through the scheduler in the thread where the scheduler
       is running. */
    if (t->completion) {
      if (t->schedule) {
	SILC_LOG_DEBUG(("Run completion through scheduler %p", t->schedule));
	if (!silc_schedule_task_add_timeout(t->schedule, t->completion,
					    t->completion_context, 0, 0)) {
	  SILC_LOG_DEBUG(("Run completion directly"));
	  t->completion(NULL, NULL, 0, 0, t->completion_context);
	}
	silc_schedule_wakeup(t->schedule);
      } else {
	SILC_LOG_DEBUG(("Run completion directly"));
	t->completion(NULL, NULL, 0, 0, t->completion_context);
      }
    }

    silc_mutex_lock(lock);
    if (t->stop)
      goto stop;

    /* Check if there are calls in queue.  Takes the most recently added
       call since new ones are added at the start of the list. */
    if (silc_list_count(t->queue) > 0) {
    execute_queue:
      silc_list_start(t->queue);
      q = silc_list_get(t->queue);

      SILC_LOG_DEBUG(("Execute call from queue"));

      /* Execute this call now */
      t->run = q->run;
      t->run_context = q->run_context;
      t->completion = q->completion;
      t->completion_context = q->completion_context;
      t->schedule = q->schedule;

      silc_list_del(t->queue, q);
      silc_list_add(t->free_queue, q);
      silc_mutex_unlock(lock);
      goto execute;
    }

    silc_mutex_unlock(lock);
    silc_mutex_lock(tp->lock);

    /* Nothing to do.  Attempt to steal call from some other thread. */
    o = silc_list_get(tp->threads);
    if (!o) {
      /* List wraps around */
      silc_list_start(tp->threads);
      o = silc_list_get(tp->threads);
    }

    /* Check that the other thread is valid and has something to execute. */
    silc_mutex_lock(o->lock);
    if (o == t || o->stop || silc_list_count(o->queue) == 0) {
      silc_mutex_unlock(o->lock);
      o = NULL;
    }

    if (o) {
      silc_mutex_unlock(tp->lock);
      silc_list_start(o->queue);
      q = silc_list_get(o->queue);

      SILC_LOG_DEBUG(("Execute call from queue from thread %p", o));

      /* Execute this call now */
      t->run = q->run;
      t->run_context = q->run_context;
      t->completion = q->completion;
      t->completion_context = q->completion_context;
      t->schedule = q->schedule;

      silc_list_del(o->queue, q);
      silc_list_add(o->free_queue, q);
      silc_mutex_unlock(o->lock);
      goto execute;
    }

    silc_mutex_lock(lock);
    if (t->stop) {
      silc_mutex_unlock(tp->lock);
      goto stop;
    }

    /* Now that we have the lock back, check the queue again. */
    if (silc_list_count(t->queue) > 0) {
      silc_mutex_unlock(tp->lock);
      goto execute_queue;
    }

    /* The thread is now free for use again. */
    t->run = NULL;
    t->completion = NULL;
    t->schedule = NULL;
    silc_list_add(tp->free_threads, t);
    silc_mutex_unlock(tp->lock);
  }

 stop:
  /* Stop the thread.  Remove from threads list. */
  SILC_LOG_DEBUG(("Stop thread %p", t));

  /* We can unlock the thread now.  After we get the thread pool lock
     no one can retrieve the thread anymore. */
  silc_mutex_unlock(lock);
  silc_mutex_lock(tp->lock);

  silc_list_del(tp->threads, t);
  silc_list_start(tp->threads);

  /* Clear thread's call queue. */
  silc_list_start(t->queue);
  silc_list_start(t->free_queue);
  while ((q = silc_list_get(t->queue)))
    silc_sfree(tp->stack, q);
  while ((q = silc_list_get(t->free_queue)))
    silc_sfree(tp->stack, q);

  /* Destroy the thread */
  silc_mutex_free(lock);
  silc_cond_free(thread_signal);
  silc_sfree(tp->stack, t);

  /* If we are last thread, signal the waiting destructor. */
  if (silc_list_count(tp->threads) == 0)
    silc_cond_signal(tp->pool_signal);

  /* Release pool reference.  Releases lock also. */
  silc_thread_pool_unref(tp);

  return NULL;
}