1 /* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
2 /* See LICENSE for licensing information */
7 extern or_options_t options
; /* command-line and config-file options */
9 static int connection_tls_finish_handshake(connection_t
*conn
);
10 static int connection_or_process_cell_from_inbuf(connection_t
*conn
);
12 /**************************************************************/
14 static void cell_pack(char *dest
, const cell_t
*src
) {
15 *(uint16_t*)dest
= htons(src
->circ_id
);
16 *(uint8_t*)(dest
+2) = src
->command
;
17 memcpy(dest
+3, src
->payload
, CELL_PAYLOAD_SIZE
);
20 static void cell_unpack(cell_t
*dest
, const char *src
) {
21 dest
->circ_id
= ntohs(*(uint16_t*)(src
));
22 dest
->command
= *(uint8_t*)(src
+2);
23 memcpy(dest
->payload
, src
+3, CELL_PAYLOAD_SIZE
);
26 /**************************************************************/
28 int connection_or_process_inbuf(connection_t
*conn
) {
30 assert(conn
&& conn
->type
== CONN_TYPE_OR
);
32 if(conn
->inbuf_reached_eof
) {
33 log_fn(LOG_INFO
,"conn reached eof. Closing.");
37 if(conn
->state
!= OR_CONN_STATE_OPEN
)
38 return 0; /* don't do anything */
39 return connection_or_process_cell_from_inbuf(conn
);
42 int connection_or_finished_flushing(connection_t
*conn
) {
45 assert(conn
&& conn
->type
== CONN_TYPE_OR
);
48 case OR_CONN_STATE_CONNECTING
:
49 if (getsockopt(conn
->s
, SOL_SOCKET
, SO_ERROR
, (void*)&e
, &len
) < 0) { /* not yet */
50 if(!ERRNO_CONN_EINPROGRESS(errno
)){
51 log_fn(LOG_DEBUG
,"in-progress connect failed. Removing.");
54 return 0; /* no change, see if next time is better */
57 /* the connect has finished. */
59 log_fn(LOG_INFO
,"OR connect() to router %s:%u finished.",
60 conn
->address
,conn
->port
);
62 if(connection_tls_start_handshake(conn
, 0) < 0)
65 case OR_CONN_STATE_OPEN
:
66 connection_stop_writing(conn
);
69 log_fn(LOG_WARN
,"BUG: called in unexpected state.");
74 /*********************/
76 void connection_or_init_conn_from_router(connection_t
*conn
, routerinfo_t
*router
) {
77 conn
->addr
= router
->addr
;
78 conn
->port
= router
->or_port
;
79 conn
->receiver_bucket
= conn
->bandwidth
= router
->bandwidth
;
80 conn
->onion_pkey
= crypto_pk_dup_key(router
->onion_pkey
);
81 conn
->link_pkey
= crypto_pk_dup_key(router
->link_pkey
);
82 conn
->identity_pkey
= crypto_pk_dup_key(router
->identity_pkey
);
83 conn
->nickname
= tor_strdup(router
->nickname
);
84 tor_free(conn
->address
);
85 conn
->address
= tor_strdup(router
->address
);
88 connection_t
*connection_or_connect(routerinfo_t
*router
) {
93 if(options
.Nickname
&& !strcmp(router
->nickname
,options
.Nickname
)) {
94 log_fn(LOG_WARN
,"You asked me to connect to myself! Failing.");
98 /* this function should never be called if we're already connected to router, but */
99 /* check first to be sure */
100 conn
= connection_exact_get_by_addr_port(router
->addr
,router
->or_port
);
104 conn
= connection_new(CONN_TYPE_OR
);
106 /* set up conn so it's got all the data we need to remember */
107 connection_or_init_conn_from_router(conn
, router
);
109 if(connection_add(conn
) < 0) { /* no space, forget it */
110 connection_free(conn
);
114 switch(connection_connect(conn
, router
->address
, router
->addr
, router
->or_port
)) {
116 connection_remove(conn
);
117 connection_free(conn
);
120 connection_set_poll_socket(conn
);
121 connection_watch_events(conn
, POLLIN
| POLLOUT
| POLLERR
);
122 /* writable indicates finish, readable indicates broken link,
123 error indicates broken link on windows */
124 conn
->state
= OR_CONN_STATE_CONNECTING
;
126 /* case 1: fall through */
129 connection_set_poll_socket(conn
);
131 if(connection_tls_start_handshake(conn
, 0) >= 0)
135 connection_remove(conn
);
136 connection_free(conn
);
140 /* ********************************** */
142 int connection_tls_start_handshake(connection_t
*conn
, int receiving
) {
143 conn
->state
= OR_CONN_STATE_HANDSHAKING
;
144 conn
->tls
= tor_tls_new(conn
->s
, receiving
);
146 log_fn(LOG_WARN
,"tor_tls_new failed. Closing.");
149 connection_start_reading(conn
);
150 log_fn(LOG_DEBUG
,"starting the handshake");
151 if(connection_tls_continue_handshake(conn
) < 0)
156 int connection_tls_continue_handshake(connection_t
*conn
) {
157 switch(tor_tls_handshake(conn
->tls
)) {
160 log_fn(LOG_INFO
,"tls error. breaking.");
163 return connection_tls_finish_handshake(conn
);
164 case TOR_TLS_WANTWRITE
:
165 connection_start_writing(conn
);
166 log_fn(LOG_DEBUG
,"wanted write");
168 case TOR_TLS_WANTREAD
: /* handshaking conns are *always* reading */
169 log_fn(LOG_DEBUG
,"wanted read");
175 static int connection_tls_finish_handshake(connection_t
*conn
) {
177 routerinfo_t
*router
;
178 char nickname
[MAX_NICKNAME_LEN
+1];
180 conn
->state
= OR_CONN_STATE_OPEN
;
181 directory_set_dirty();
182 connection_watch_events(conn
, POLLIN
);
183 log_fn(LOG_DEBUG
,"tls handshake done. verifying.");
184 if (! tor_tls_peer_has_cert(conn
->tls
)) { /* It's an OP. */
185 if (options
.ORPort
) { /* I'm an OR; good. */
186 conn
->receiver_bucket
= conn
->bandwidth
= DEFAULT_BANDWIDTH_OP
;
188 } else { /* Neither side sent a certificate: ouch. */
189 log_fn(LOG_WARN
,"Neither peer sent a cert! Closing.");
193 /* Okay; the other side is an OR. */
194 if (tor_tls_get_peer_cert_nickname(conn
->tls
, nickname
, MAX_NICKNAME_LEN
)) {
195 log_fn(LOG_WARN
,"Other side (%s:%d) has a cert without a valid nickname. Closing.",
196 conn
->address
, conn
->port
);
199 log_fn(LOG_DEBUG
, "Other side claims to be '%s'", nickname
);
200 pk
= tor_tls_verify(conn
->tls
);
202 log_fn(LOG_WARN
,"Other side '%s' (%s:%d) has a cert but it's invalid. Closing.",
203 nickname
, conn
->address
, conn
->port
);
206 router
= router_get_by_link_pk(pk
);
208 log_fn(LOG_WARN
,"Unrecognized public key from peer '%s' (%s:%d). Closing.",
209 nickname
, conn
->address
, conn
->port
);
210 crypto_free_pk_env(pk
);
213 if(conn
->link_pkey
) { /* I initiated this connection. */
214 if(crypto_pk_cmp_keys(conn
->link_pkey
, pk
)) {
215 log_fn(LOG_WARN
,"We connected to '%s' (%s:%d) but he gave us a different key. Closing.",
216 nickname
, conn
->address
, conn
->port
);
217 crypto_free_pk_env(pk
);
220 log_fn(LOG_DEBUG
,"The router's pk matches the one we meant to connect to. Good.");
222 if(connection_exact_get_by_addr_port(router
->addr
,router
->or_port
)) {
223 log_fn(LOG_INFO
,"Router %s is already connected. Dropping.", router
->nickname
);
224 crypto_free_pk_env(pk
);
227 connection_or_init_conn_from_router(conn
, router
);
229 crypto_free_pk_env(pk
);
230 if (strcmp(conn
->nickname
, nickname
)) {
231 log_fn(LOG_WARN
,"Other side claims to be '%s', but we wanted '%s'",
232 nickname
, conn
->nickname
);
235 if (!options
.ORPort
) { /* If I'm an OP... */
236 conn
->receiver_bucket
= conn
->bandwidth
= DEFAULT_BANDWIDTH_OP
;
237 circuit_n_conn_open(conn
); /* send the pending creates, if any. */
242 /* ********************************** */
244 void connection_or_write_cell_to_buf(const cell_t
*cellp
, connection_t
*conn
) {
245 char networkcell
[CELL_NETWORK_SIZE
];
246 char *n
= networkcell
;
248 assert(connection_speaks_cells(conn
));
252 connection_write_to_buf(n
, CELL_NETWORK_SIZE
, conn
);
255 /* if there's a whole cell there, pull it off and process it. */
256 static int connection_or_process_cell_from_inbuf(connection_t
*conn
) {
257 char buf
[CELL_NETWORK_SIZE
];
261 log_fn(LOG_DEBUG
,"%d: starting, inbuf_datalen %d (%d pending in tls object).",
262 conn
->s
,(int)buf_datalen(conn
->inbuf
),tor_tls_get_pending_bytes(conn
->tls
));
263 if(buf_datalen(conn
->inbuf
) < CELL_NETWORK_SIZE
) /* entire response available? */
264 return 0; /* not yet */
266 connection_fetch_from_buf(buf
, CELL_NETWORK_SIZE
, conn
);
268 /* retrieve cell info from buf (create the host-order struct from the
269 * network-order string) */
270 cell_unpack(&cell
, buf
);
272 command_process_cell(&cell
, conn
);
274 goto loop
; /* process the remainder of the buffer */