1 /* $OpenBSD: connection.c,v 1.25 2025/01/28 20:41:44 claudio Exp $ */
2
3 /*
4 * Copyright (c) 2009 Claudio Jeker <claudio@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/socket.h>
22 #include <sys/uio.h>
23
24 #include <netinet/in.h>
25 #include <netinet/tcp.h>
26
27 #include <scsi/iscsi.h>
28
29 #include <errno.h>
30 #include <event.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35
36 #include "iscsid.h"
37 #include "log.h"
38
39 void conn_dispatch(int, short, void *);
40 void conn_write_dispatch(int, short, void *);
41
42 int c_do_connect(struct connection *, enum c_event);
43 int c_do_login(struct connection *, enum c_event);
44 int c_do_loggedin(struct connection *, enum c_event);
45 int c_do_req_logout(struct connection *, enum c_event);
46 int c_do_logout(struct connection *, enum c_event);
47 int c_do_loggedout(struct connection *, enum c_event);
48 int c_do_fail(struct connection *, enum c_event);
49 int c_do_cleanup(struct connection *, enum c_event);
50
51 const char *conn_state(int);
52 const char *conn_event(enum c_event);
53
54 void
conn_new(struct session * s,struct connection_config * cc)55 conn_new(struct session *s, struct connection_config *cc)
56 {
57 struct connection *c;
58 int nodelay = 1;
59
60 if (!(c = calloc(1, sizeof(*c))))
61 fatal("session_add_conn");
62
63 c->fd = -1;
64 c->state = CONN_FREE;
65 c->session = s;
66 c->cid = arc4random();
67 c->config = *cc;
68 c->mine = initiator_conn_defaults;
69 if (s->config.HeaderDigest != 0)
70 c->mine.HeaderDigest = s->config.HeaderDigest;
71 if (s->config.DataDigest != 0)
72 c->mine.DataDigest = s->config.DataDigest;
73 c->his = iscsi_conn_defaults;
74
75 c->sev.sess = s;
76 c->sev.conn = c;
77 evtimer_set(&c->sev.ev, session_fsm_callback, &c->sev);
78
79 TAILQ_INIT(&c->pdu_w);
80 TAILQ_INIT(&c->tasks);
81 TAILQ_INSERT_TAIL(&s->connections, c, entry);
82
83 if (pdu_readbuf_set(&c->prbuf, PDU_READ_SIZE)) {
84 log_warn("conn_new");
85 conn_free(c);
86 return;
87 }
88
89 /* create socket */
90 c->fd = socket(c->config.TargetAddr.ss_family, SOCK_STREAM, 0);
91 if (c->fd == -1) {
92 log_warn("conn_new: socket");
93 conn_free(c);
94 return;
95 }
96 if (socket_setblockmode(c->fd, 1)) {
97 log_warn("conn_new: socket_setblockmode");
98 conn_free(c);
99 return;
100 }
101
102 /* try to turn off TCP Nagle */
103 if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &nodelay,
104 sizeof(nodelay)) == -1)
105 log_warn("conn_new: setting TCP_NODELAY");
106
107 event_set(&c->ev, c->fd, EV_READ|EV_PERSIST, conn_dispatch, c);
108 event_set(&c->wev, c->fd, EV_WRITE, conn_write_dispatch, c);
109
110 conn_fsm(c, CONN_EV_CONNECT);
111 }
112
113 void
conn_free(struct connection * c)114 conn_free(struct connection *c)
115 {
116 log_debug("conn_free");
117
118 pdu_readbuf_free(&c->prbuf);
119 pdu_free_queue(&c->pdu_w);
120
121 event_del(&c->sev.ev);
122 event_del(&c->ev);
123 event_del(&c->wev);
124 if (c->fd != -1)
125 close(c->fd);
126
127 taskq_cleanup(&c->tasks);
128
129 TAILQ_REMOVE(&c->session->connections, c, entry);
130 free(c);
131 }
132
133 void
conn_dispatch(int fd,short event,void * arg)134 conn_dispatch(int fd, short event, void *arg)
135 {
136 struct connection *c = arg;
137 ssize_t n;
138
139 if (!(event & EV_READ)) {
140 log_debug("spurious read call");
141 return;
142 }
143 if ((n = pdu_read(c)) == -1) {
144 if (errno == EAGAIN || errno == ENOBUFS ||
145 errno == EINTR) /* try later */
146 return;
147 log_warn("pdu_read");
148 conn_fsm(c, CONN_EV_FAIL);
149 return;
150 }
151 if (n == 0) { /* connection closed */
152 conn_fsm(c, CONN_EV_CLOSED);
153 return;
154 }
155
156 pdu_parse(c);
157 }
158
159 void
conn_write_dispatch(int fd,short event,void * arg)160 conn_write_dispatch(int fd, short event, void *arg)
161 {
162 struct connection *c = arg;
163 ssize_t n;
164 int error;
165 socklen_t len;
166
167 if (!(event & EV_WRITE)) {
168 log_debug("spurious write call");
169 return;
170 }
171
172 switch (c->state) {
173 case CONN_XPT_WAIT:
174 len = sizeof(error);
175 if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR,
176 &error, &len) == -1 || (errno = error)) {
177 log_warn("connect to %s failed",
178 log_sockaddr(&c->config.TargetAddr));
179 conn_fsm(c, CONN_EV_FAIL);
180 return;
181 }
182 conn_fsm(c, CONN_EV_CONNECTED);
183 break;
184 default:
185 if ((n = pdu_write(c)) == -1) {
186 log_warn("pdu_write");
187 conn_fsm(c, CONN_EV_FAIL);
188 return;
189 }
190 if (n == 0) { /* connection closed */
191 conn_fsm(c, CONN_EV_CLOSED);
192 return;
193 }
194
195 /* check if there is more to send */
196 if (pdu_pending(c))
197 event_add(&c->wev, NULL);
198 }
199 }
200
201 void
conn_fail(struct connection * c)202 conn_fail(struct connection *c)
203 {
204 log_debug("conn_fail");
205 conn_fsm(c, CONN_EV_FAIL);
206 }
207
208 int
conn_task_ready(struct connection * c)209 conn_task_ready(struct connection *c)
210 {
211 if ((c->state & CONN_RUNNING) && TAILQ_EMPTY(&c->tasks))
212 return 1;
213 return 0;
214 }
215
216 void
conn_task_issue(struct connection * c,struct task * t)217 conn_task_issue(struct connection *c, struct task *t)
218 {
219 TAILQ_INSERT_TAIL(&c->tasks, t, entry);
220 conn_task_schedule(c);
221 }
222
223 void
conn_task_schedule(struct connection * c)224 conn_task_schedule(struct connection *c)
225 {
226 struct task *t = TAILQ_FIRST(&c->tasks);
227 struct pdu *p, *np;
228
229 if (!t) {
230 log_debug("conn_task_schedule: task is hiding");
231 return;
232 }
233
234 /* move pdus to the write queue */
235 for (p = TAILQ_FIRST(&t->sendq); p != NULL; p = np) {
236 np = TAILQ_NEXT(p, entry);
237 TAILQ_REMOVE(&t->sendq, p, entry);
238 conn_pdu_write(c, p);
239 }
240 if (t->callback == NULL) {
241 /* no callback, immediate command expecting no answer */
242 conn_task_cleanup(c, t);
243 free(t);
244 }
245 }
246
247 void
conn_task_cleanup(struct connection * c,struct task * t)248 conn_task_cleanup(struct connection *c, struct task *t)
249 {
250 pdu_free_queue(&t->sendq);
251 pdu_free_queue(&t->recvq);
252 /* XXX need some state to know if queued or not */
253 if (c) {
254 TAILQ_REMOVE(&c->tasks, t, entry);
255 if (!TAILQ_EMPTY(&c->tasks))
256 conn_task_schedule(c);
257 else
258 session_schedule(c->session);
259 }
260 }
261
262 #define SET_NUM(p, x, v, min, max) \
263 do { \
264 if (!strcmp((p)->key, #v)) { \
265 (x)->his.v = text_to_num((p)->value, (min), (max), &err); \
266 if (err) { \
267 log_warnx("bad param %s=%s: %s", \
268 (p)->key, (p)->value, err); \
269 errors++; \
270 } \
271 } \
272 } while (0)
273
274 #define SET_BOOL(p, x, v) \
275 do { \
276 if (!strcmp((p)->key, #v)) { \
277 (x)->his.v = text_to_bool((p)->value, &err); \
278 if (err) { \
279 log_warnx("bad param %s=%s: %s", \
280 (p)->key, (p)->value, err); \
281 errors++; \
282 } \
283 } \
284 } while (0)
285
286 #define SET_DIGEST(p, x, v) \
287 do { \
288 if (!strcmp((p)->key, #v)) { \
289 (x)->his.v = text_to_digest((p)->value, &err); \
290 if (err) { \
291 log_warnx("bad param %s=%s: %s", \
292 (p)->key, (p)->value, err); \
293 errors++; \
294 } \
295 } \
296 } while (0)
297
298 int
conn_parse_kvp(struct connection * c,struct kvp * kvp)299 conn_parse_kvp(struct connection *c, struct kvp *kvp)
300 {
301 struct kvp *k;
302 struct session *s = c->session;
303 const char *err;
304 int errors = 0;
305
306
307 for (k = kvp; k->key; k++) {
308 log_debug("conn_parse_kvp: %s = %s", k->key, k->value);
309 /* XXX handle NotUnderstood|Irrelevant|Reject */
310 SET_NUM(k, s, MaxBurstLength, 512, 16777215);
311 SET_NUM(k, s, FirstBurstLength, 512, 16777215);
312 SET_NUM(k, s, DefaultTime2Wait, 0, 3600);
313 SET_NUM(k, s, DefaultTime2Retain, 0, 3600);
314 SET_NUM(k, s, MaxOutstandingR2T, 1, 65535);
315 SET_NUM(k, s, TargetPortalGroupTag, 0, 65535);
316 SET_NUM(k, s, MaxConnections, 1, 65535);
317 SET_BOOL(k, s, InitialR2T);
318 SET_BOOL(k, s, ImmediateData);
319 SET_BOOL(k, s, DataPDUInOrder);
320 SET_BOOL(k, s, DataSequenceInOrder);
321 SET_NUM(k, s, ErrorRecoveryLevel, 0, 2);
322 SET_NUM(k, c, MaxRecvDataSegmentLength, 512, 16777215);
323 SET_DIGEST(k, c, HeaderDigest);
324 SET_DIGEST(k, c, DataDigest);
325 }
326
327 if (errors) {
328 log_warnx("conn_parse_kvp: errors found");
329 return -1;
330 }
331 return 0;
332 }
333
334 #undef SET_NUM
335 #undef SET_BOOL
336 #undef SET_DIGEST
337
338 #define GET_BOOL_P(dst, req, k, src, f) \
339 do { \
340 if (f == 0 && !strcmp(req, #k)) { \
341 (dst)->key = #k; \
342 (dst)->value = ((src)->mine.k) ? "Yes" : "No"; \
343 f++; \
344 } \
345 } while (0)
346
347 #define GET_DIGEST_P(dst, req, k, src, f) \
348 do { \
349 if (f == 0 && !strcmp(req, #k)) { \
350 (dst)->key = #k; \
351 (dst)->value = \
352 ((src)->mine.k == DIGEST_NONE) ? "None" : "CRC32C,None";\
353 f++; \
354 } \
355 } while (0)
356
357 #define GET_NUM_P(dst, req, k, src, f, e) \
358 do { \
359 if (f == 0 && !strcmp(req, #k)) { \
360 (dst)->key = #k; \
361 if (asprintf(&((dst)->value), "%u", (src)->mine.k) == -1)\
362 e++; \
363 else \
364 (dst)->flags |= KVP_VALUE_ALLOCED; \
365 f++; \
366 } \
367 } while (0)
368
369 #define GET_STR_C(dst, req, k, src, f) \
370 do { \
371 if (f == 0 && !strcmp(req, #k)) { \
372 (dst)->key = #k; \
373 (dst)->value = (src)->config.k; \
374 f++; \
375 } \
376 } while (0)
377
378 #define GET_STYPE_C(dst, req, k, src, f) \
379 do { \
380 if (f == 0 && !strcmp(req, #k)) { \
381 (dst)->key = #k; \
382 (dst)->value = ((src)->config.k == SESSION_TYPE_DISCOVERY)\
383 ? "Discovery" : "Normal"; \
384 f++; \
385 } \
386 } while (0)
387
388 int
kvp_set_from_mine(struct kvp * kvp,const char * key,struct connection * c)389 kvp_set_from_mine(struct kvp *kvp, const char *key, struct connection *c)
390 {
391 int e = 0, f = 0;
392
393 if (kvp->flags & KVP_KEY_ALLOCED)
394 free(kvp->key);
395 kvp->key = NULL;
396 if (kvp->flags & KVP_VALUE_ALLOCED)
397 free(kvp->value);
398 kvp->value = NULL;
399 kvp->flags = 0;
400
401 /* XXX handle at least CHAP */
402 if (!strcmp(key, "AuthMethod")) {
403 kvp->key = "AuthMethod";
404 kvp->value = "None";
405 return 0;
406 }
407 GET_DIGEST_P(kvp, key, HeaderDigest, c, f);
408 GET_DIGEST_P(kvp, key, DataDigest, c, f);
409 GET_NUM_P(kvp, key, MaxConnections, c->session, f, e);
410 GET_STR_C(kvp, key, TargetName, c->session, f);
411 GET_STR_C(kvp, key, InitiatorName, c->session, f);
412 GET_BOOL_P(kvp, key, InitialR2T, c->session, f);
413 GET_BOOL_P(kvp, key, ImmediateData, c->session, f);
414 GET_NUM_P(kvp, key, MaxRecvDataSegmentLength, c, f, e);
415 GET_NUM_P(kvp, key, MaxBurstLength, c->session, f, e);
416 GET_NUM_P(kvp, key, FirstBurstLength, c->session, f, e);
417 GET_NUM_P(kvp, key, DefaultTime2Wait, c->session, f, e);
418 GET_NUM_P(kvp, key, DefaultTime2Retain, c->session, f, e);
419 GET_NUM_P(kvp, key, MaxOutstandingR2T, c->session, f, e);
420 GET_BOOL_P(kvp, key, DataPDUInOrder, c->session, f);
421 GET_BOOL_P(kvp, key, DataSequenceInOrder, c->session, f);
422 GET_NUM_P(kvp, key, ErrorRecoveryLevel, c->session, f, e);
423 GET_STYPE_C(kvp, key, SessionType, c->session, f);
424 /* XXX handle TaskReporting */
425
426 if (f == 0) {
427 errno = EINVAL;
428 return 1;
429 }
430
431 return e;
432 }
433
434 #undef GET_BOOL_P
435 #undef GET_DIGEST_P
436 #undef GET_NUM_P
437 #undef GET_STR_C
438 #undef GET_STYPE_C
439
440 void
conn_pdu_write(struct connection * c,struct pdu * p)441 conn_pdu_write(struct connection *c, struct pdu *p)
442 {
443 struct iscsi_pdu *ipdu;
444
445 /* XXX I GUESS THIS SHOULD BE MOVED TO PDU SOMEHOW... */
446 ipdu = pdu_getbuf(p, NULL, PDU_HEADER);
447 switch (ISCSI_PDU_OPCODE(ipdu->opcode)) {
448 case ISCSI_OP_I_NOP:
449 case ISCSI_OP_SCSI_REQUEST:
450 case ISCSI_OP_TASK_REQUEST:
451 case ISCSI_OP_LOGIN_REQUEST:
452 case ISCSI_OP_TEXT_REQUEST:
453 case ISCSI_OP_DATA_OUT:
454 case ISCSI_OP_LOGOUT_REQUEST:
455 case ISCSI_OP_SNACK_REQUEST:
456 ipdu->expstatsn = ntohl(c->expstatsn);
457 break;
458 }
459
460 TAILQ_INSERT_TAIL(&c->pdu_w, p, entry);
461 event_add(&c->wev, NULL);
462 }
463
464 /* connection state machine more or less as specified in the RFC */
465 struct {
466 int state;
467 enum c_event event;
468 int (*action)(struct connection *, enum c_event);
469 } fsm[] = {
470 { CONN_FREE, CONN_EV_CONNECT, c_do_connect }, /* T1 */
471 { CONN_XPT_WAIT, CONN_EV_CONNECTED, c_do_login }, /* T4 */
472 { CONN_IN_LOGIN, CONN_EV_LOGGED_IN, c_do_loggedin }, /* T5 */
473 { CONN_LOGGED_IN, CONN_EV_LOGOUT, c_do_logout }, /* T9 */
474 { CONN_LOGGED_IN, CONN_EV_REQ_LOGOUT, c_do_req_logout },/* T11 */
475 { CONN_LOGOUT_REQ, CONN_EV_LOGOUT, c_do_logout }, /* T10 */
476 { CONN_LOGOUT_REQ, CONN_EV_REQ_LOGOUT, c_do_req_logout},/* T12 */
477 { CONN_LOGOUT_REQ, CONN_EV_LOGGED_OUT, c_do_loggedout },/* T18 */
478 { CONN_IN_LOGOUT, CONN_EV_LOGGED_OUT, c_do_loggedout }, /* T13 */
479 { CONN_IN_LOGOUT, CONN_EV_REQ_LOGOUT, c_do_req_logout },/* T14 */
480 { CONN_CLEANUP_WAIT, CONN_EV_CLEANING_UP, c_do_cleanup},/* M2 */
481 { CONN_CLEANUP_WAIT, CONN_EV_FREE, c_do_loggedout }, /* M1 */
482 { CONN_IN_CLEANUP, CONN_EV_FREE, c_do_loggedout }, /* M4 */
483 { CONN_IN_CLEANUP, CONN_EV_CLEANING_UP, c_do_cleanup},
484 /* either one of T2, T7, T15, T16, T17, M3 */
485 { CONN_ANYSTATE, CONN_EV_CLOSED, c_do_fail },
486 { CONN_ANYSTATE, CONN_EV_FAIL, c_do_fail },
487 { CONN_ANYSTATE, CONN_EV_FREE, c_do_fail },
488 { 0, 0, NULL }
489 };
490
491 void
conn_fsm(struct connection * c,enum c_event event)492 conn_fsm(struct connection *c, enum c_event event)
493 {
494 int i, ns;
495
496 for (i = 0; fsm[i].action != NULL; i++) {
497 if (c->state & fsm[i].state && event == fsm[i].event) {
498 log_debug("conn_fsm[%s]: %s ev %s",
499 c->session->config.SessionName,
500 conn_state(c->state), conn_event(event));
501 ns = fsm[i].action(c, event);
502 if (ns == -1)
503 /* XXX better please */
504 fatalx("conn_fsm: action failed");
505 log_debug("conn_fsm[%s]: new state %s",
506 c->session->config.SessionName, conn_state(ns));
507 c->state = ns;
508 return;
509 }
510 }
511 log_warnx("conn_fsm[%s]: unhandled state transition [%s, %s]",
512 c->session->config.SessionName, conn_state(c->state),
513 conn_event(event));
514 fatalx("bork bork bork");
515 }
516
517 int
c_do_connect(struct connection * c,enum c_event ev)518 c_do_connect(struct connection *c, enum c_event ev)
519 {
520 if (c->fd == -1) {
521 log_warnx("connect(%s), lost socket",
522 log_sockaddr(&c->config.TargetAddr));
523 session_fsm(&c->sev, SESS_EV_CONN_FAIL, 0);
524 return CONN_FREE;
525 }
526 if (c->config.LocalAddr.ss_len != 0) {
527 if (bind(c->fd, (struct sockaddr *)&c->config.LocalAddr,
528 c->config.LocalAddr.ss_len) == -1) {
529 log_warn("bind(%s)",
530 log_sockaddr(&c->config.LocalAddr));
531 session_fsm(&c->sev, SESS_EV_CONN_FAIL, 0);
532 return CONN_FREE;
533 }
534 }
535 if (connect(c->fd, (struct sockaddr *)&c->config.TargetAddr,
536 c->config.TargetAddr.ss_len) == -1) {
537 if (errno == EINPROGRESS) {
538 event_add(&c->wev, NULL);
539 event_add(&c->ev, NULL);
540 return CONN_XPT_WAIT;
541 } else {
542 log_warn("connect(%s)",
543 log_sockaddr(&c->config.TargetAddr));
544 session_fsm(&c->sev, SESS_EV_CONN_FAIL, 0);
545 return CONN_FREE;
546 }
547 }
548 event_add(&c->ev, NULL);
549 /* move forward */
550 return c_do_login(c, CONN_EV_CONNECTED);
551 }
552
553 int
c_do_login(struct connection * c,enum c_event ev)554 c_do_login(struct connection *c, enum c_event ev)
555 {
556 /* start a login session and hope for the best ... */
557 initiator_login(c);
558 return CONN_IN_LOGIN;
559 }
560
561 int
c_do_loggedin(struct connection * c,enum c_event ev)562 c_do_loggedin(struct connection *c, enum c_event ev)
563 {
564 iscsi_merge_conn_params(&c->active, &c->mine, &c->his);
565 session_fsm(&c->sev, SESS_EV_CONN_LOGGED_IN, 0);
566
567 return CONN_LOGGED_IN;
568 }
569
570 int
c_do_req_logout(struct connection * c,enum c_event ev)571 c_do_req_logout(struct connection *c, enum c_event ev)
572 {
573 /* target requested logout. XXX implement async handler */
574
575 if (c->state & CONN_IN_LOGOUT)
576 return CONN_IN_LOGOUT;
577 else
578 return CONN_LOGOUT_REQ;
579 }
580
581 int
c_do_logout(struct connection * c,enum c_event ev)582 c_do_logout(struct connection *c, enum c_event ev)
583 {
584 /* logout is in progress ... */
585 return CONN_IN_LOGOUT;
586 }
587
588 int
c_do_loggedout(struct connection * c,enum c_event ev)589 c_do_loggedout(struct connection *c, enum c_event ev)
590 {
591 /*
592 * Called by the session fsm before calling conn_free.
593 * Doing this so the state transition is logged.
594 */
595 return CONN_FREE;
596 }
597
598 int
c_do_fail(struct connection * c,enum c_event ev)599 c_do_fail(struct connection *c, enum c_event ev)
600 {
601 log_debug("c_do_fail");
602
603 /* cleanup events so that the connection does not retrigger */
604 event_del(&c->ev);
605 event_del(&c->wev);
606 close(c->fd);
607 c->fd = -1; /* make sure this fd is not closed again */
608
609 /* all pending task have failed so clean them up */
610 taskq_cleanup(&c->tasks);
611
612 /* session will take care of cleaning up the mess */
613 session_fsm(&c->sev, SESS_EV_CONN_FAIL, 0);
614
615 if (ev == CONN_EV_FREE || c->state & CONN_NEVER_LOGGED_IN)
616 return CONN_FREE;
617 return CONN_CLEANUP_WAIT;
618 }
619
620 int
c_do_cleanup(struct connection * c,enum c_event ev)621 c_do_cleanup(struct connection *c, enum c_event ev)
622 {
623 /* nothing to do here just adjust state */
624 return CONN_IN_CLEANUP;
625 }
626
627 const char *
conn_state(int s)628 conn_state(int s)
629 {
630 static char buf[15];
631
632 switch (s) {
633 case CONN_FREE:
634 return "FREE";
635 case CONN_XPT_WAIT:
636 return "XPT_WAIT";
637 case CONN_XPT_UP:
638 return "XPT_UP";
639 case CONN_IN_LOGIN:
640 return "IN_LOGIN";
641 case CONN_LOGGED_IN:
642 return "LOGGED_IN";
643 case CONN_IN_LOGOUT:
644 return "IN_LOGOUT";
645 case CONN_LOGOUT_REQ:
646 return "LOGOUT_REQ";
647 case CONN_CLEANUP_WAIT:
648 return "CLEANUP_WAIT";
649 case CONN_IN_CLEANUP:
650 return "IN_CLEANUP";
651 default:
652 snprintf(buf, sizeof(buf), "UKNWN %x", s);
653 return buf;
654 }
655 /* NOTREACHED */
656 }
657
658 const char *
conn_event(enum c_event e)659 conn_event(enum c_event e)
660 {
661 static char buf[15];
662
663 switch (e) {
664 case CONN_EV_FAIL:
665 return "fail";
666 case CONN_EV_CONNECT:
667 return "connect";
668 case CONN_EV_CONNECTED:
669 return "connected";
670 case CONN_EV_LOGGED_IN:
671 return "logged in";
672 case CONN_EV_REQ_LOGOUT:
673 return "logout requested";
674 case CONN_EV_LOGOUT:
675 return "logout";
676 case CONN_EV_LOGGED_OUT:
677 return "logged out";
678 case CONN_EV_CLEANING_UP:
679 return "cleaning up";
680 case CONN_EV_CLOSED:
681 return "closed";
682 case CONN_EV_FREE:
683 return "forced free";
684 }
685
686 snprintf(buf, sizeof(buf), "UKNWN %d", e);
687 return buf;
688 }
689