Handling TCP out-of-band data correctly

I wrote a simple client and server to work with out-of-band data. The client just sends a single out of band data to the server and the server uses SIGURG to handle this single byte. The server also should handle normal traffic in an infinite loop. The code has a race condition which does not work as expected. Sometimes I get an "Invalid argument" from a call to recv() in the SIGURG handler. Another question I have is that should I block SIGURG signal when calling accept? Also, which one is the preferred scenario:

My last question is, since the client sends the out-of-band data immediately, is there a chance for the server to receive the SIGURG just after the completion of three-way handshake, but before returning from accept? If so, I think the "clifd" var can has an invalid value when it is used in the SIGURG handler.

the code for the client:

#include "myheader.h"

int main(int argc, char *argv[])
{
    struct sockaddr_in saddr;
    int sockfd;

    const char c = 'a';

    if (2 != argc)
    {
        fprintf(stderr, "Usage: %s ipaddr\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    if (-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
        die("sockfd()");

    (void)memset(&saddr, 0, sizeof(saddr));

    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(PORT);

    if (-1 == inet_pton(AF_INET, argv[1], &saddr.sin_addr))
        die("inet_pton()");

    if (-1 == connect(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)))
        die("connect()");

//  if (-1 == send(sockfd, "HELLO\n", 6, 0))
//      die("send()");

    if (-1 == send(sockfd, &c, 1, MSG_OOB))
        die("send()");

    close(sockfd);

    return 0;
}

and the code for the server:

#include "myheader.h"

void sigurg_handler(int);

char    oob;
int sockfd, clifd;

int main(void)
{
    struct sockaddr_in myaddr;
    char buf[BUFSIZ];
    ssize_t nbytes;
    sigset_t sset, oset;
    sigemptyset(&sset);
    sigaddset(&sset, SIGURG);

    if (-1 == (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
        die("socket()");

    (void)memset(&myaddr, 0, sizeof(myaddr));

    myaddr.sin_family = AF_INET;
    myaddr.sin_port = htons(PORT);
    myaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (-1 == bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr)))
        die("bind()");

    if (-1 == listen(sockfd, BACKLOG))
        die("listen()");

    if (-1 == fcntl(sockfd, F_SETOWN, getpid()))
        die("fcntl()");

    if (SIG_ERR == signal(SIGURG, sigurg_handler))
            die("signal()");
    for (;;)
    {
        /* block SIGURG when accepting the connection */
//      sigprocmask(SIG_SETMASK, &sset, &oset);
        printf("bloking in accept()\n");
        if (-1 == (clifd = accept(sockfd, NULL, NULL)))
            die("accept()");

        /* unblock SIGURG */
//      sigprocmask(SIG_SETMASK, &oset, NULL);

        printf("recv()ing normal data\n");
        nbytes = recv(clifd, buf, sizeof(buf), 0);
        buf[nbytes] = 0; /* null-terminate */

        printf("%s", buf);

    }

    close(sockfd);
}

void
sigurg_handler(int signo)
{
    char buff[100];
    ssize_t nbytes;

    printf("SIGURG received\n");
    if (clifd != 0)
    {
        if (-1 == (nbytes = recv(clifd, buff, sizeof(buff) - 1, MSG_OOB)))
            die("recv() in sigurg_handler()");

        buff[nbytes] = 0;
        printf("from sigurg_handler: received \"%s\"\n", buff);
    }
    else
    {
        printf("clifd = %d\n", clifd);
        exit(1);
    }
}

Example:

> ./serv 
bloking in accept()         /* first client */
SIGURG received
from sigurg_handler: received "a"
recv()ing normal data
bloking in accept()         /* second client */
SIGURG received
recv() in sigurg_handler(): Invalid argument
> ./serv             /* third client */
bloking in accept()
SIGURG received
clifd = 0
>

Upvotes: 3

Views: 1723

Answers (1)

yurenchen
yurenchen

Reputation: 2483

I heard select() third parameter can handle tcp OOB

ret = select(connfd+1,&read_fds,NULL,&exceptions_fds,NULL);

https://blog.csdn.net/ty_laurel/article/details/52164669
https://github.com/ty92/OOB



select() exceptional

use select really can avoid the signal setup step,
so that you not miss the oob (that before signal setup).

https://man7.org/linux/man-pages/man7/tcp.7.html#:~:text=out%2Dof%2Dband%20data%20is%20present

man 2 select_tut has a demo code
https://man7.org/linux/man-pages/man2/select_tut.2.html#:~:text=read%20OOB%20data,-before


limitation

but if you did't read the oob byte in time, when new oob arrive, old oob byte become normal data (inserted as normal data into the stream), even if SO_OOBINLINE not set (on linux)
//that behavior may difference in various tcp stack.

https://man7.org/linux/man-pages/man7/tcp.7.html#:~:text=limited%20support%20for%20out%2Dof%2Dband



PS: you'd better copy links with :~:text= manually, it'll highlight the keyword in chrome.
// or click in edit preview mode.
// in normal page stackoverflow always encode ~ in url, which will invalidate the anchor

// those man pages still not support anchors to this day, it's pity.

Upvotes: 0

Related Questions