Exemplo n.º 1
0
/*
 * our version of brpc_call(). We cache in portnumber in to->sin_port for
 * your convenience. to and from addresses are taken and received in network
 * order.
 */
enum clnt_stat
brpc_call(
	rpcprog_t	prog,		/* rpc program number to call. */
	rpcvers_t	vers,		/* rpc program version */
	rpcproc_t	proc,		/* rpc procedure to call */
	xdrproc_t	in_xdr,		/* routine to serialize arguments */
	caddr_t		args,		/* arg vector for remote call */
	xdrproc_t	out_xdr,	/* routine to deserialize results */
	caddr_t		ret,		/* addr of buf to place results in */
	int		rexmit,		/* retransmission interval (secs) */
	int		wait_time,	/* how long (secs) to wait (resp) */
	struct sockaddr_in 	*to,		/* destination */
	struct sockaddr_in	*from_who,	/* responder's port/address */
	uint_t			auth)		/* type of auth wanted. */
{
	int s;
	char hostname[MAXHOSTNAMELEN];
	struct sockaddr_in from;	/* us. */
	socklen_t from_len;
	XDR xmit_xdrs, rcv_xdrs;	/* xdr memory */
	AUTH *xmit_auth;		/* our chosen auth cookie */
	gid_t fake_gids = 1;		/* fake gids list for auth_unix */
	caddr_t trm_msg, rcv_msg;	/* outgoing/incoming rpc mesgs */
	struct rpc_msg reply;		/* our reply msg header */
	int trm_len, rcv_len;
	struct rpc_err rpc_error;	/* to store RPC errors in on rcv. */
	static uint_t xid;		/* current xid */
	uint_t xmit_len;		/* How much of the buffer we used */
	int nrefreshes = 2;		/* # of times to refresh cred */
	int flags = 0;			/* send flags */
	uint_t xdelay;
	int errors, preserve_errno;
	uint32_t timeout;
	socklen_t optlen;

	xmit_auth = NULL;

	trm_len = mac_get_mtu();
	trm_msg = bkmem_alloc(trm_len);
	rcv_msg = bkmem_alloc(NFSBUF_SIZE);

	if (trm_msg == NULL || rcv_msg == NULL) {
		errno = ENOMEM;
		rpc_error.re_status = RPC_CANTSEND;
		goto gt_error;
	}

	if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
		rpc_error.re_status = RPC_CANTSEND;
		goto gt_error;
	}

	if (dontroute) {
		(void) setsockopt(s, SOL_SOCKET, SO_DONTROUTE,
		    (const void *)&dontroute, sizeof (dontroute));
	}

	if (to->sin_addr.s_addr == cached_destination.s_addr) {
		optlen = sizeof (timeout);
		(void) getsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (void *)&timeout,
		    &optlen);
	} else {
		cached_destination.s_addr = htonl(INADDR_ANY);
	}

	/* Bind our endpoint. */
	from.sin_family = AF_INET;
	ipv4_getipaddr(&from.sin_addr);
	from.sin_addr.s_addr = htonl(from.sin_addr.s_addr);
	from.sin_port = get_source_port(B_TRUE);

	if (bind(s, (struct sockaddr *)&from, sizeof (from)) < 0) {
		rpc_error.re_status = RPC_CANTSEND;
		goto gt_error;
	}

	bzero((caddr_t)&rpc_error, sizeof (struct rpc_err));

	/* initialize reply's rpc_msg struct, so we can decode later. */
	reply.acpted_rply.ar_verf = _null_auth;	/* struct copy */
	reply.acpted_rply.ar_results.where = ret;
	reply.acpted_rply.ar_results.proc = out_xdr;

	if (ntohs(to->sin_port) == 0) {
		/* snag the udp port we need. */
		if ((to->sin_port = (in_port_t)bpmap_getport(prog, vers,
		    &(rpc_error.re_status), to, NULL)) == 0)
			goto gt_error;
		to->sin_port = htons(to->sin_port);
	}

	/* generate xid - increment */
	if (xid == 0)
		xid = (uint_t)(prom_gettime() / 1000) + 1;
	else
		xid++;

	/* set up outgoing pkt as xdr modified. */
	xdrmem_create(&xmit_xdrs, trm_msg, trm_len, XDR_ENCODE);

	/* setup rpc header */
	if (rpc_hdr(&xmit_xdrs, xid, prog, vers, proc) != TRUE) {
		dprintf("brpc_call: cannot setup rpc header.\n");
		rpc_error.re_status = RPC_FAILED;
		goto gt_error;
	}

	/* setup authentication */
	switch (auth) {
	case AUTH_NONE:
		xmit_auth = authnone_create();
		break;
	case AUTH_UNIX:
		/*
		 * Assumes we've configured the stack and thus know our
		 * IP address/hostname, either by using DHCP or rarp/bootparams.
		 */
		gethostname(hostname, sizeof (hostname));
		xmit_auth = authunix_create(hostname, 0, 1, 1, &fake_gids);
		break;
	default:
		dprintf("brpc_call: Unsupported authentication type: %d\n",
		    auth);
		rpc_error.re_status = RPC_AUTHERROR;
		goto gt_error;
	/*NOTREACHED*/
	}

	/*
	 * rpc_hdr puts everything in the xmit buffer for the header
	 * EXCEPT the proc. Put it, and our authentication info into
	 * it now, serializing as we go. We will be at the place where
	 * we left off.
	 */
	xmit_xdrs.x_op = XDR_ENCODE;
	if ((XDR_PUTINT32(&xmit_xdrs, (int32_t *)&proc) == FALSE) ||
	    (AUTH_MARSHALL(xmit_auth, &xmit_xdrs, NULL) == FALSE) ||
	    ((*in_xdr)(&xmit_xdrs, args) == FALSE)) {
		rpc_error.re_status = RPC_CANTENCODEARGS;
		goto gt_error;
	} else
		xmit_len = (int)XDR_GETPOS(&xmit_xdrs); /* for sendto */

	/*
	 * Right now the outgoing packet should be all serialized and
	 * ready to go... Set up timers.
	 */

	xdelay = (rexmit == 0) ? RPC_REXMIT_MSEC : (rexmit * 1000);
	(void) setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (void *)&xdelay,
	    sizeof (xdelay));
	wait_time = (wait_time == 0) ? RPC_RCVWAIT_MSEC : (wait_time * 1000);

	wait_time += prom_gettime();

	/*
	 * send out the request. The first item in the receive buffer will
	 * be the xid. Check if it is correct.
	 */
	errors = 0;
	rpc_error.re_status = RPC_TIMEDOUT;
	do {
		if (sendto(s, trm_msg, xmit_len, flags, (struct sockaddr *)to,
		    sizeof (struct sockaddr_in)) < 0) {
			/*
			 * If errno is set to ETIMEDOUT, return
			 * with RPC status as RPC_TIMEDOUT. Calling
			 * funciton will take care of this error by
			 * retrying the RPC call.
			 */
			if (errno == ETIMEDOUT) {
				rpc_error.re_status = RPC_TIMEDOUT;
			} else {
				rpc_error.re_status = RPC_CANTSEND;
			}
			goto gt_error;
		}

		from_len = sizeof (struct sockaddr_in);
		while ((rcv_len = recvfrom(s, rcv_msg, NFSBUF_SIZE,
		    MSG_DONTWAIT, (struct sockaddr *)from_who,
		    &from_len)) > 0 || errors < RPC_ALLOWABLE_ERRORS) {
			if (rcv_len < 0) {
				if (errno == EWOULDBLOCK ||
				    errno == ETIMEDOUT) {
					break; /* timeout */
				}
				rpc_error.re_status = RPC_CANTRECV;
				goto gt_error;
			}
			if (ntohl(*((uint32_t *)(rcv_msg))) != xid) {
				dprintf("brpc_call: xid: 0x%x != 0x%x\n",
				    *(uint32_t *)(rcv_msg), xid);
				continue;
			}
			/*
			 * Let's deserialize the data into our 'ret' buffer.
			 */
			xdrmem_create(&rcv_xdrs, rcv_msg, rcv_len, XDR_DECODE);
			if (xdr_replymsg(&rcv_xdrs, &reply) == FALSE) {
				rpc_error.re_status = RPC_CANTDECODERES;
				goto gt_error;
			}
			_seterr_reply(&reply, &rpc_error);
			switch (rpc_error.re_status) {
			case RPC_SUCCESS:
				/*
				 * XXX - validate for unix and none
				 * always return true.
				 */
				if (AUTH_VALIDATE(xmit_auth,
				    &reply.acpted_rply.ar_verf) == FALSE) {
					rpc_error.re_status = RPC_AUTHERROR;
					rpc_error.re_why = AUTH_INVALIDRESP;
					errors++;
				}
				if (reply.acpted_rply.ar_verf.oa_base !=
				    0) {
					xmit_xdrs.x_op = XDR_FREE;
					(void) xdr_opaque_auth(
					    &xmit_xdrs,
					    &reply.acpted_rply.ar_verf);
				}
				break;

			case RPC_AUTHERROR:
				/*
				 * Let's see if our credentials need
				 * refreshing
				 */
				if (nrefreshes > 0 && AUTH_REFRESH(xmit_auth,
				    NULL, NULL)) {
					nrefreshes--;
				}
				errors++;
				break;

			case RPC_PROCUNAVAIL:
				/*
				 * Might be a silly portmapper implementation
				 * erroneously responding to our rpc broadcast
				 * indirect portmapper call. For this
				 * particular case, we don't increment the
				 * error counter because we want to keep
				 * sifting for successful replies...
				 */
				if (to->sin_addr.s_addr !=
				    ntohl(INADDR_BROADCAST))
					errors++;
				break;

			case RPC_PROGVERSMISMATCH:
				/*
				 * Successfully talked to server, but they
				 * don't speak our lingo.
				 */
				goto gt_error;

			default:
				/* Just keep trying till there's no data... */
				errors++;
				break;
			}

			if (rpc_error.re_status != RPC_SUCCESS) {
				dprintf("brpc_call: from: %s, error: ",
				    inet_ntoa(from_who->sin_addr));
				rpc_disperr(&rpc_error);
			} else
				break;
		}

		/*
		 * If we're having trouble reassembling datagrams, let the
		 * application know ASAP so that it can take the appropriate
		 * actions.
		 */

	} while (rpc_error.re_status != RPC_SUCCESS && errno != ETIMEDOUT &&
	    prom_gettime() < wait_time);

gt_error:
	if (xmit_auth != NULL)
		AUTH_DESTROY(xmit_auth);

	if (trm_msg != NULL)
		bkmem_free(trm_msg, trm_len);
	if (rcv_msg != NULL)
		bkmem_free(rcv_msg, NFSBUF_SIZE);

	if (rpc_error.re_status != RPC_SUCCESS)
		rpc_disperr(&rpc_error);

	/*
	 * socket calls reset errno. Since we want to hold onto the errno
	 * value if it is ETIMEDOUT to communicate to our caller that this
	 * RPC_TIMEDOUT situation is due to a stack problem (we're getting
	 * a reply, but the stack simply can't assemble it.), we need to
	 * preserve errno's value over the socket_close().
	 */
	preserve_errno = (errno == ETIMEDOUT) ? errno : 0;
	(void) socket_close(s);
	errno = preserve_errno;

	return (rpc_error.re_status);
}
Exemplo n.º 2
0
int main(int argc, char *argv[]) {
  int ret, exitcode;
  mbedtls_net_context server_fd;
  uint32_t flags;
  char scenario[10000] = "";
  char server_host[100] = "";
  char server_port[6] = "";
  char server_ssl_hostname[100] = "";
  const char *pers = "dtls_client";
  int opt;
  struct timeval t0, t1;

  mbedtls_entropy_context entropy;
  mbedtls_ctr_drbg_context ctr_drbg;
  mbedtls_ssl_context ssl;
  mbedtls_ssl_config conf;
  mbedtls_x509_crt cacert;
  mbedtls_timing_delay_context timer;

  /* Parse command line */
  while ((opt = getopt(argc, argv, "h:n:p:s:")) != -1) {
    switch (opt) {
      case 'h':
        strncpy(server_host, optarg, sizeof(server_host));
        break;
      case 'n':
        strncpy(server_ssl_hostname, optarg, sizeof(server_ssl_hostname));
        break;
      case 'p':
        strncpy(server_port, optarg, sizeof(server_port));
        break;
      case 's':
        strncpy(scenario, optarg, sizeof(scenario));
        break;
      default:                 /* '?' */
        print_usage(argv[0]);
    }
  }

  if (!(scenario[0] && server_port[0] && server_host[0])) {
    print_usage(argv[0]);
  }

  if (!server_ssl_hostname[0]) {
    strncpy(server_ssl_hostname, server_host, sizeof(server_ssl_hostname));
  }
#if defined(MBEDTLS_DEBUG_C)
  mbedtls_debug_set_threshold(DEBUG_LEVEL);
#endif

  /*
   * 0. Initialize the RNG and the session data
   */
  mbedtls_net_init(&server_fd);
  mbedtls_ssl_init(&ssl);
  mbedtls_ssl_config_init(&conf);
  mbedtls_x509_crt_init(&cacert);
  mbedtls_ctr_drbg_init(&ctr_drbg);

  plog("Seeding the random number generator...");

  mbedtls_entropy_init(&entropy);
  if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
                                   (const unsigned char *)pers, strlen(pers))) != 0) {
    plog("ERROR: failed! mbedtls_ctr_drbg_seed returned %d", ret);
    goto exit;
  }

  /*
   * 0. Load certificates
   */
  plog("Loading the CA root certificate ...");

  ret = mbedtls_x509_crt_parse(&cacert, (const unsigned char *)mbedtls_test_cas_pem,
                               mbedtls_test_cas_pem_len);
  if (ret < 0) {
    plog("ERROR: failed! mbedtls_x509_crt_parse returned -0x%x", -ret);
    goto exit;
  }

  plog("Connecting to udp %s:%s (SSL hostname: %s)...",
       server_host, server_port, server_ssl_hostname);

  if ((ret = mbedtls_net_connect(&server_fd, server_host, server_port, MBEDTLS_NET_PROTO_UDP)) != 0) {
    plog("ERROR: failed! mbedtls_net_connect returned %d", ret);
    goto exit;
  }

  plog("The local client UDP source port is %d", get_source_port(server_fd.fd));

  plog("Setting up the DTLS structure...");

  if ((ret = mbedtls_ssl_config_defaults(&conf,
                                         MBEDTLS_SSL_IS_CLIENT,
                                         MBEDTLS_SSL_TRANSPORT_DATAGRAM,
                                         MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
    plog("ERROR: failed! mbedtls_ssl_config_defaults returned %d", ret);
    goto exit;
  }

  /* OPTIONAL is usually a bad choice for security, but makes interop easier
   * in this simplified example, in which the ca chain is hardcoded.
   * Production code should set a proper ca chain and use REQUIRED. */
  mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
  mbedtls_ssl_conf_ca_chain(&conf, &cacert, NULL);
  mbedtls_ssl_conf_rng(&conf, mbedtls_ctr_drbg_random, &ctr_drbg);
  mbedtls_ssl_conf_dbg(&conf, log_mbedtls_debug_callback, NULL);
  /* TODO timeouts */

  if ((ret = mbedtls_ssl_setup(&ssl, &conf)) != 0) {
    plog("ERROR: failed! mbedtls_ssl_setup returned %d", ret);
    goto exit;
  }

  if ((ret = mbedtls_ssl_set_hostname(&ssl, server_ssl_hostname)) != 0) {
    plog("ERROR: failed! mbedtls_ssl_set_hostname returned %d", ret);
    goto exit;
  }

  mbedtls_ssl_set_bio(&ssl, &server_fd,
                      mbedtls_net_send, mbedtls_net_recv, mbedtls_net_recv_timeout);

  mbedtls_ssl_set_timer_cb(&ssl, &timer, mbedtls_timing_set_delay, mbedtls_timing_get_delay);

  plog("Performing the SSL/TLS handshake...");

  gettimeofday(&t0, NULL);
  do {
    ret = mbedtls_ssl_handshake(&ssl);
    plog(" ... during SSL handshake, ret=%d (WANT_READ=%d, WANT_WRITE=%d, RECV_FAILED=%d",
         ret, MBEDTLS_ERR_SSL_WANT_READ, MBEDTLS_ERR_SSL_WANT_WRITE, MBEDTLS_ERR_NET_RECV_FAILED);
    gettimeofday(&t1, NULL);
  } while ((duration_ms(&t1, &t0) <= SSL_HANDSHAKE_TIMEOUT_MILLISECS) &&
           (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE));

  plog("handshake duration: %d milliseconds", duration_ms(&t1, &t0));
  if (duration_ms(&t1, &t0) > SSL_HANDSHAKE_TIMEOUT_MILLISECS) {
    plog("ERROR: long time to perform handshake: %d milliseconds", duration_ms(&t1, &t0));
    ret = MBEDTLS_ERR_SSL_TIMEOUT;
    goto exit;
  }

  if (ret != 0) {
    plog("ERROR: failed! mbedtls_ssl_handshake returned -0x%x", -ret);
    goto exit;
  }

  plog("Verifying peer X.509 certificate...");

  /* In real life, we would have used MBEDTLS_SSL_VERIFY_REQUIRED so that the
   * handshake would not succeed if the peer's cert is bad.  Even if we used
   * MBEDTLS_SSL_VERIFY_OPTIONAL, we would bail out here if ret != 0 */
  if ((flags = mbedtls_ssl_get_verify_result(&ssl)) != 0) {
    char vrfy_buf[512];
    mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), "! ", flags);
    plog("Verification failed: %s", vrfy_buf);
  } else {
    plog("Certificates ok");
  }

  ret = run_scenario(scenario, &ssl);
  if (ret != 0) {
    goto exit;
  }

  plog("Closing the connection...");

  /* No error checking, the connection might be closed already */
  do {
    ret = mbedtls_ssl_close_notify(&ssl);
  } while (ret == MBEDTLS_ERR_SSL_WANT_WRITE);
  ret = 0;

exit:

#ifdef MBEDTLS_ERROR_C
  if (ret != 0) {
    char error_buf[100];
    mbedtls_strerror(ret, error_buf, 100);
    plog("ERROR: Last error was: %d - %s", ret, error_buf);
  }
#endif

  mbedtls_net_free(&server_fd);

  mbedtls_x509_crt_free(&cacert);
  mbedtls_ssl_free(&ssl);
  mbedtls_ssl_config_free(&conf);
  mbedtls_ctr_drbg_free(&ctr_drbg);
  mbedtls_entropy_free(&entropy);

  exitcode = ret == 0 ? 0 : 1;
  plog("Done, exitcode=%d", exitcode);
  return exitcode;
}