+uint16_t utcp_get_mtu(struct utcp *utcp) {
+ return utcp ? utcp->mtu : 0;
+}
+
+void utcp_set_mtu(struct utcp *utcp, uint16_t mtu) {
+ // TODO: handle overhead of the header
+ if(utcp) {
+ utcp->mtu = mtu;
+ }
+}
+
+void utcp_reset_timers(struct utcp *utcp) {
+ if(!utcp) {
+ return;
+ }
+
+ struct timeval now, then;
+
+ gettimeofday(&now, NULL);
+
+ then = now;
+
+ then.tv_sec += utcp->timeout;
+
+ for(int i = 0; i < utcp->nconnections; i++) {
+ struct utcp_connection *c = utcp->connections[i];
+
+ if(c->reapable) {
+ continue;
+ }
+
+ c->rtrx_timeout = now;
+ c->conn_timeout = then;
+ c->rtt_start.tv_sec = 0;
+ }
+
+ if(utcp->rto > START_RTO) {
+ utcp->rto = START_RTO;
+ }
+}
+
+int utcp_get_user_timeout(struct utcp *u) {
+ return u ? u->timeout : 0;
+}
+
+void utcp_set_user_timeout(struct utcp *u, int timeout) {
+ if(u) {
+ u->timeout = timeout;
+ }
+}
+
+size_t utcp_get_sndbuf(struct utcp_connection *c) {
+ return c ? c->sndbuf.maxsize : 0;
+}
+
+size_t utcp_get_sndbuf_free(struct utcp_connection *c) {
+ if(!c) {
+ return 0;
+ }
+
+ switch(c->state) {
+ case SYN_SENT:
+ case SYN_RECEIVED:
+ case ESTABLISHED:
+ case CLOSE_WAIT:
+ return buffer_free(&c->sndbuf);
+
+ default:
+ return 0;
+ }
+}
+
+void utcp_set_sndbuf(struct utcp_connection *c, size_t size) {
+ if(!c) {
+ return;
+ }
+
+ c->sndbuf.maxsize = size;
+
+ if(c->sndbuf.maxsize != size) {
+ c->sndbuf.maxsize = -1;
+ }
+}
+
+size_t utcp_get_rcvbuf(struct utcp_connection *c) {
+ return c ? c->rcvbuf.maxsize : 0;
+}
+
+size_t utcp_get_rcvbuf_free(struct utcp_connection *c) {
+ if(c && (c->state == ESTABLISHED || c->state == CLOSE_WAIT)) {
+ return buffer_free(&c->rcvbuf);
+ } else {
+ return 0;
+ }
+}
+
+void utcp_set_rcvbuf(struct utcp_connection *c, size_t size) {
+ if(!c) {
+ return;
+ }
+
+ c->rcvbuf.maxsize = size;
+
+ if(c->rcvbuf.maxsize != size) {
+ c->rcvbuf.maxsize = -1;
+ }
+}
+
+size_t utcp_get_sendq(struct utcp_connection *c) {
+ return c->sndbuf.used;
+}
+
+size_t utcp_get_recvq(struct utcp_connection *c) {
+ return c->rcvbuf.used;
+}
+
+bool utcp_get_nodelay(struct utcp_connection *c) {
+ return c ? c->nodelay : false;
+}
+
+void utcp_set_nodelay(struct utcp_connection *c, bool nodelay) {
+ if(c) {
+ c->nodelay = nodelay;
+ }
+}
+
+bool utcp_get_keepalive(struct utcp_connection *c) {
+ return c ? c->keepalive : false;
+}
+
+void utcp_set_keepalive(struct utcp_connection *c, bool keepalive) {
+ if(c) {
+ c->keepalive = keepalive;
+ }
+}
+
+size_t utcp_get_outq(struct utcp_connection *c) {
+ return c ? seqdiff(c->snd.nxt, c->snd.una) : 0;
+}
+
+void utcp_set_recv_cb(struct utcp_connection *c, utcp_recv_t recv) {
+ if(c) {
+ c->recv = recv;
+ }
+}
+
+void utcp_set_poll_cb(struct utcp_connection *c, utcp_poll_t poll) {
+ if(c) {
+ c->poll = poll;
+ }
+}
+
+void utcp_set_accept_cb(struct utcp *utcp, utcp_accept_t accept, utcp_pre_accept_t pre_accept) {
+ if(utcp) {
+ utcp->accept = accept;
+ utcp->pre_accept = pre_accept;
+ }
+}
+
+void utcp_expect_data(struct utcp_connection *c, bool expect) {
+ if(!c || c->reapable) {
+ return;
+ }
+
+ if(!(c->state == ESTABLISHED || c->state == FIN_WAIT_1 || c->state == FIN_WAIT_2)) {
+ return;
+ }
+
+ if(expect) {
+ // If we expect data, start the connection timer.
+ if(!timerisset(&c->conn_timeout)) {
+ gettimeofday(&c->conn_timeout, NULL);
+ c->conn_timeout.tv_sec += c->utcp->timeout;
+ }
+ } else {
+ // If we want to cancel expecting data, only clear the timer when there is no unACKed data.
+ if(c->snd.una == c->snd.last) {
+ timerclear(&c->conn_timeout);
+ }
+ }
+}
+
+void utcp_offline(struct utcp *utcp, bool offline) {
+ for(int i = 0; i < utcp->nconnections; i++) {
+ struct utcp_connection *c = utcp->connections[i];
+
+ if(!c->reapable) {
+ utcp_expect_data(c, offline);
+
+ // If we are online again, reset the retransmission timers, but keep the connection timeout as it is,
+ // to prevent peers toggling online/offline state frequently from keeping connections alive
+ // if there is no progress in sending actual data.
+ if(!offline) {
+ gettimeofday(&utcp->connections[i]->rtrx_timeout, NULL);
+ utcp->connections[i]->rtt_start.tv_sec = 0;
+ }
+ }
+ }
+
+ if(!offline && utcp->rto > START_RTO) {
+ utcp->rto = START_RTO;
+ }