terom@2: terom@2: #include "sock_gnutls.h" terom@2: terom@2: #include terom@2: #include terom@2: terom@10: // XXX: errors terom@10: static err_t sock_gnutls_read (struct sock_stream *base_sock, void *buf, size_t *len) terom@10: { terom@10: struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls); terom@12: struct error_info *err = SOCK_GNUTLS_ERR(sock); terom@10: int ret; terom@10: terom@12: // read gnutls record terom@12: ret = gnutls_record_recv(sock->session, buf, *len); terom@10: terom@12: // errors terom@12: if (ret < 0 && ret != GNUTLS_E_AGAIN) terom@12: RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret); terom@12: terom@12: else if (ret == 0) terom@12: return SET_ERROR(err, ERR_READ_EOF); terom@12: terom@12: terom@12: // eagain? terom@14: if (ret < 0) { terom@12: *len = 0; terom@12: terom@12: } else { terom@12: // updated length terom@12: *len = ret; terom@12: terom@12: } terom@10: terom@10: return SUCCESS; terom@10: } terom@10: terom@10: static err_t sock_gnutls_write (struct sock_stream *base_sock, const void *buf, size_t *len) terom@10: { terom@10: struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls); terom@12: struct error_info *err = SOCK_GNUTLS_ERR(sock); terom@10: int ret; terom@12: terom@12: // read gnutls record terom@12: ret = gnutls_record_send(sock->session, buf, *len); terom@10: terom@12: // errors terom@12: if (ret < 0 && ret != GNUTLS_E_AGAIN) terom@12: RETURN_SET_ERROR_EXTRA(err, ERR_GNUTLS_RECORD_RECV, ret); terom@10: terom@12: else if (ret == 0) terom@12: return SET_ERROR(err, ERR_READ_EOF); terom@12: terom@12: terom@12: // eagain? terom@14: if (ret < 0) { terom@12: *len = 0; terom@12: terom@12: } else { terom@12: // updated length terom@12: *len = ret; terom@12: } terom@10: terom@10: return SUCCESS; terom@10: } terom@10: terom@12: static void sock_gnutls_event_handler (int fd, short what, void *arg) terom@12: { terom@12: struct sock_gnutls *sock = arg; terom@27: terom@27: (void) fd; terom@27: (void) what; terom@12: terom@12: // gnutls might be able to proceed now, so ask user to try what didn't work before now, using the mask given to terom@12: // event_enable(). terom@12: sock_stream_invoke_callbacks(SOCK_GNUTLS_BASE(sock), sock->ev_mask); terom@12: } terom@12: terom@10: static err_t sock_gnutls_event_init (struct sock_stream *base_sock) terom@2: { terom@2: struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls); terom@12: terom@12: err_t err; terom@12: terom@12: // set nonblocking terom@12: if ((err = sock_tcp_set_nonblock(SOCK_GNUTLS_TCP(sock), 1))) terom@12: return err; terom@12: terom@12: // add ourselves as the event handler terom@12: if ((err = sock_tcp_init_ev(SOCK_GNUTLS_TCP(sock), &sock_gnutls_event_handler, sock))) terom@12: return err; terom@12: terom@12: // ok terom@10: return SUCCESS; terom@2: } terom@2: terom@10: static err_t sock_gnutls_event_enable (struct sock_stream *base_sock, short mask) terom@2: { terom@2: struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls); terom@12: int ret; terom@2: terom@12: // store the ev_mask. We don't care about it here, because we assume that event_enable is only called once read or terom@12: // write, respectively, return zero. This is really the only case we can handle with gnutls. terom@12: sock->ev_mask = mask; terom@12: terom@12: // gnutls_record_get_direction tells us what I/O operation gnutls would have required for the last terom@12: // operation, so we can use that to determine what events to register terom@12: switch ((ret = gnutls_record_get_direction(sock->session))) { terom@12: case 0: terom@12: // read more data terom@12: sock_tcp_add_event(SOCK_GNUTLS_TCP(sock), EV_READ); terom@12: break; terom@12: terom@12: case 1: terom@12: // write buffer full terom@12: sock_tcp_add_event(SOCK_GNUTLS_TCP(sock), EV_WRITE); terom@12: break; terom@12: terom@12: default: terom@12: // random error terom@12: RETURN_SET_ERROR_EXTRA(SOCK_GNUTLS_ERR(sock), ERR_GNUTLS_RECORD_GET_DIRECTION, ret); terom@12: } terom@12: terom@12: // ok... wait terom@10: return SUCCESS; terom@2: } terom@2: terom@28: static void sock_gnutls_release (struct sock_stream *base_sock) terom@28: { terom@28: struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls); terom@28: terom@28: // close the session rudely terom@28: // XXX: does this actually do everything we need it to? Don't want to call gnutls_bye here, since we're void... terom@28: gnutls_deinit(sock->session); terom@28: terom@28: // terminate the TCP transport terom@28: sock_tcp_close(SOCK_GNUTLS_TCP(sock)); terom@28: terom@28: // free terom@28: free(sock); terom@28: } terom@28: terom@2: /* terom@2: * Our sock_stream_Type terom@2: */ terom@2: struct sock_stream_type sock_gnutls_type = { terom@27: .methods = { terom@27: .read = &sock_gnutls_read, terom@27: .write = &sock_gnutls_write, terom@27: .event_init = &sock_gnutls_event_init, terom@27: .event_enable = &sock_gnutls_event_enable, terom@28: .release = &sock_gnutls_release, terom@27: }, terom@2: }; terom@2: terom@2: /* terom@2: * XXX: global shared sock_gnutls_ctx terom@2: */ terom@2: struct sock_gnutls_client_ctx _sock_gnutls_client_ctx; terom@2: terom@2: /* terom@2: * Configure the given gnutls socket context to use simple anonymous client credentials terom@2: */ terom@4: static err_t sock_gnutls_client_ctx_anon (struct sock_gnutls_client_ctx *ctx, struct error_info *err) terom@2: { terom@4: // init to use anonymous x509 cert terom@4: if ((ERROR_EXTRA(err) = gnutls_certificate_allocate_credentials(&ctx->xcred)) < 0) terom@4: return SET_ERROR(err, ERR_GNUTLS_CERT_ALLOC_CRED); terom@4: terom@4: // done terom@4: return SUCCESS; terom@2: } terom@2: terom@14: // XXX: log func terom@14: void _log (int level, const char *msg) terom@14: { terom@27: printf("gnutls: %d: %s", level, msg); terom@14: } terom@14: terom@5: err_t sock_gnutls_global_init (struct error_info *err) terom@2: { terom@2: // global init terom@4: if ((ERROR_EXTRA(err) = gnutls_global_init()) < 0) terom@4: return SET_ERROR(err, ERR_GNUTLS_GLOBAL_INIT); terom@2: terom@2: // init _sock_gnutls_ctx terom@4: if (sock_gnutls_client_ctx_anon(&_sock_gnutls_client_ctx, err)) terom@4: return ERROR_CODE(err); terom@3: terom@14: // XXX: debug terom@14: // gnutls_global_set_log_function(&_log); terom@14: // gnutls_global_set_log_level(11); terom@14: terom@3: // done terom@3: return SUCCESS; terom@2: } terom@2: terom@2: terom@14: err_t sock_ssl_connect (struct sock_stream **sock_ptr, const char *host, const char *service, struct error_info *err) terom@2: { terom@5: struct sock_gnutls *sock = NULL; terom@2: struct sock_gnutls_client_ctx *ctx = &_sock_gnutls_client_ctx; terom@2: terom@2: // alloc terom@2: if ((sock = calloc(1, sizeof(*sock))) == NULL) terom@5: return SET_ERROR(err, ERR_CALLOC); terom@2: terom@5: // initialize base terom@5: sock_stream_init(SOCK_GNUTLS_BASE(sock), &sock_gnutls_type); terom@2: terom@2: // initialize client session terom@5: if ((ERROR_EXTRA(err) = gnutls_init(&sock->session, GNUTLS_CLIENT)) < 0) terom@5: JUMP_SET_ERROR(err, ERR_GNUTLS_INIT); terom@2: terom@2: // ...default priority stuff terom@5: if ((ERROR_EXTRA(err) = gnutls_set_default_priority(sock->session))) terom@5: JUMP_SET_ERROR(err, ERR_GNUTLS_SET_DEFAULT_PRIORITY); terom@2: terom@2: // bind anon credentials terom@5: if ((ERROR_EXTRA(err) = gnutls_credentials_set(sock->session, GNUTLS_CRD_CERTIFICATE, ctx->xcred))) terom@5: JUMP_SET_ERROR(err, ERR_GNUTLS_CRED_SET); terom@2: terom@2: // TCP connect terom@5: if (sock_tcp_init_connect(SOCK_GNUTLS_TCP(sock), host, service)) terom@5: JUMP_SET_ERROR_INFO(err, SOCK_GNUTLS_ERR(sock)); terom@2: terom@2: // bind default transport functions (recv/send) to use the TCP fd terom@27: gnutls_transport_set_ptr(sock->session, (gnutls_transport_ptr_t) (long int) sock->base_tcp.fd); terom@2: terom@2: // perform the handshake terom@5: if ((ERROR_EXTRA(err) = gnutls_handshake(sock->session)) < 0) terom@5: JUMP_SET_ERROR(err, ERR_GNUTLS_HANDSHAKE); terom@2: terom@2: // done terom@5: *sock_ptr = SOCK_GNUTLS_BASE(sock); terom@5: terom@5: return SUCCESS; terom@5: terom@5: error: terom@5: // XXX: cleanup terom@5: terom@5: return ERROR_CODE(err); terom@2: } terom@2: