Professional Web Applications Themes

Little C socket help required - UNIX Programming

Hi everyone, I am trying to write a simple c application that will connect to my smtp server, and send a email to a recipent. What I dont understand I how I can wait for a response from a server, before sending the next command. I assume I would do this via a loop. Could someone provide me a example of a loop that waits for a response before sending the next set of data please? Thanks in advance Mick...

  1. #1

    Default Little C socket help required

    Hi everyone,

    I am trying to write a simple c application that will connect to my smtp
    server, and send a email to a recipent.

    What I dont understand I how I can wait for a response from a server,
    before sending the next command.

    I assume I would do this via a loop.

    Could someone provide me a example of a loop that waits for a response
    before sending the next set of data please?

    Thanks in advance
    Mick
    Materialised Guest

  2. #2

    Default Re: Little C socket help required

    Materialised <net> writes:
     

    Read the SMTP RFC!

    You could implement it naively with:

    fprintf(socket,"%s%c%c",command,13,10);
    do{
    fgets(line,sizeof(line),socket);
    }while(line[3]!=' ');


    --
    __Pascal_Bourguignon__ http://www.informatimago.com/
    There is no worse tyranny than to force a man to pay for what he doesn't
    want merely because you think it would be good for him.--Robert Heinlein
    http://www.theadvocates.org/
    Pascal Guest

  3. #3

    Default Re: Little C socket help required

    Pascal Bourguignon wrote: 
    >
    >
    > Read the SMTP RFC!
    >
    > You could implement it naively with:
    >
    > fprintf(socket,"%s%c%c",command,13,10);
    > do{
    > fgets(line,sizeof(line),socket);
    > }while(line[3]!=' ');
    >
    >[/ref]
    I have read the rfc
    Here is what I have so far, it is sloppy as hell, im just looking for a
    way to make it batter.

    int x;
    for(x = 1; x > 800; ++x) {
    printf("%d\n", x);

    switch(x) {
    case 100:
    strcpy(data, "Helo\n");
    len = strlen(data);
    if(sendall(sockfd, data, &len) == -1) {
    perror("sendall");
    fprintf(stderr, "We only send %d bytes because of the error");
    }
    break;
    case 200:
    strcpy(data, "mail from:com\n");
    len = strlen(data);
    if(sendall(sockfd, data, &len) == -1) {
    perror("sendall");
    fprintf(stderr, "We only send %d bytes because of the error");
    }
    break;
    case 300:
    strcpy(data, "rcpt to:org\n");
    len = strlen(data);
    if(sendall(sockfd, data, &len) == -1) {
    perror("sendall");
    fprintf(stderr, "We only send %d bytes because of the error");
    }
    break;
    case 400:strcpy(data, "data\n");
    len = strlen(data);

    if(sendall(sockfd, data, &len) == -1) {
    perror("sendall");
    fprintf(stderr, "We only send %d bytes because of the error");
    }
    break;
    case 500: strcpy(data, "Hello how are you??\n\n.\n");
    len = strlen(data);

    if(sendall(sockfd, data, &len) == -1) {
    perror("sendall");
    fprintf(stderr, "We only send %d bytes because of the error");
    }
    break;
    }

    if((numbytes=recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
    perror("recv");
    exit(1);
    }

    buf[numbytes] = '\0';
    printf("Recieved: %s\n", buf);
    printf("%s\n", data);
    }


    sendall is defined as

    int sendall(int s, char *buf, int *len)
    {
    int total =0;
    int bytesleft = *len;
    int n;

    while(total < *len) {
    n = send(s, buf+total, bytesleft, 0);
    if( n == -1) {
    break;
    }
    total += n;
    bytesleft -= n;
    }

    *len = total; // return number actually sent here
    return n==-1?-1:0; // return -1 on failure and 0 on sucess
    }
    --
    Materialised

    Please note the email address this message uses in its headers is a spam
    trap.
    If you wish to contact me, you can do so at the following address:
    bWlja0Bjb2RlZ3VydXMub3Jn

    www.CodeGurus.org
    Materialised Guest

  4. #4

    Default Re: Little C socket help required

    Materialised <net> writes:
     
    > > Read the SMTP RFC!
    > > You could implement it naively with:
    > > fprintf(socket,"%s%c%c",command,13,10);
    > > do{
    > > fgets(line,sizeof(line),socket);
    > > }while(line[3]!=' ');
    > >[/ref]
    > I have read the rfc
    > Here is what I have so far, it is sloppy as hell, im just looking for
    > a way to make it batter.
    >
    > int x;
    > for(x = 1; x > 800; ++x) {
    > printf("%d\n", x);
    >
    > switch(x) {
    > case 100:[/ref]

    So, you will try to receive 99 packets from the server before sending
    Helo. Is that what the SMTP RFC specified???
     

    What is \n ???

    If you had read the SMTP RFC, you should know now that what's used to
    end lines is CR LF, not \n!
     

    So, the cut-and-paste feature works well. Good. It's always nice to
    see that one's editor works correctly. Too bad the brains don't.
     

    See below about packets/stream.
     

    And??? Why don't you p the response from the server? Don't you
    think it may be interesting to know whether the server agrees to work
    with you?
     

    If you'd read the SMTP RFC, you would know that SMTP runs on TCP, not
    on UDP, that is it does not work with messages (packets), but with a
    stream of byte (lines separated with CR LF).

     

    That means that if you cannot send all you bytes in one system call,
    you can (and should!) send the rest instead of aborting.

    You could use printf or write too.
     

    On the other hand, you may want to put a timeout on sending (or
    receiving) data:

    signal(SIGALRM,timeout_handler);
    alarm(smtp_timeout);
    fprintf(socket,"%s%c%c",line,13,10);
    fflush(socket);
    alarm(0);
    signal(SIGALRM,SIG_DFL);


    --
    __Pascal_Bourguignon__ http://www.informatimago.com/
    There is no worse tyranny than to force a man to pay for what he doesn't
    want merely because you think it would be good for him.--Robert Heinlein
    http://www.theadvocates.org/
    Pascal Guest

  5. #5

    Default Re: Little C socket help required

    Hi,

    read about the select call. You can wait for a response on your file
    descriptor.

    Johan


    "Materialised" <net> schreef in bericht
    news:c0ocp5$18ira5$news.uni-berlin.de... 


    Johan Guest

  6. #6

    Default Re: Little C socket help required

    Johan wrote: [/ref]

    Materialized (poor sod, I'd sue my parents) doesn't need "select". You
    need select when you want to either wait for only a predefined time
    ("timeout") or have multiple I/O channels to manage, e.g. in a parallel
    server process.

    When dealing with a single server instance, and an SMTP server is a
    simple example, you can just stick to a "send(request);
    receieve(response); inspect(response);"-loop. The initial function
    depends on e.g. whether your server will greet you with a "response".

    Sometimes, you don't even need a loop, because when you e.g. want to
    send data to a server which sends responses, you can just code straight
    away:

    int send_data(int sd, void *data, size_t size)
    {
    char resp[BUFSIZ];
    long code;
    if (size) write(sd, data, size);
    read(sd, resp, BUFSIZ);
    code = strtol(data);
    return code < 400;
    }
    sd = connect(...);
    if (!send_data(sd, NULL, 0))
    exit(1);
    if (!send_data(sd, "HELO USC-ISIF.ARPA\r\n", ...))
    exit(2);
    if (!send_data(sd, "MAIL FROM:<ARPA>\r\n", ...))
    exit(2);
    if (!send_data(sd, "RCPT TO:<ARPA>\r\n", ...))
    exit(3);
    if (!send_data(sd, "RCPT TO:<ARPA>\r\n", ...))
    exit(4);
    if (!send_data(sd, "RCPT TO:<ARPA>\r\n", ...))
    exit(5);
    if (!send_data(sd, "DATA\r\n", 6))
    exit(6);
    if (!send_data(sd, buffer, strlen(buffer)))
    exit(7);
    if (!send_data(sd, "QUIT\r\n", 6))
    exit(8);
    close(sd);
    (scenario 1 from rfc0821).

    --
    Josef Möllers (Pinguinpfleger bei FSC)
    If failure had no penalty success would not be a prize
    -- T. Pratchett
    Josef Guest

  7. #7

    Default Re: Little C socket help required

    Materialised <net> writes:
     

    To implement protocols, you should have some notion of (finite) state
    machines.

    You have states and transitions. The machine stays in a state. Upon
    reception of an event, a transition moves the machine to another
    state. You can associate actions to the transitions, or to the entry
    or the exit of states.

    An introduction to State Diagrams in UML:
    http://www.developer.com/design/article.php/2238131
    http://www.agilemodeling.com/artifacts/stateMachineDiagram.htm



    The RFC informally doents such state machines.


    A server and a client normally have each its own state machine, they
    don't use the same (they are not in the same state, and don't receive
    the same events, obviously).


    The definition of events may be more difficult since they're not all
    of the same kind: some events are some data received (read) from the
    communication medium, some events are time out (signals, counters),
    some events are mere conditions. When events can occur
    asynchronously, you may need to manage an event queue, but for usual
    protocols, the events are basically embodied in the lines sent and
    received, so you can easily process them one by one.


    A state machine does not need to be implemented as a loop. It depends
    on how you store its state. A state can be stored in a pure data
    item: an enum or an integer encoding the state, for example. Or it can
    be stored in a pure control flow item: the instruction the program is
    being executing encodes the state.

    For example, assuming you've identified the following states for a
    SMTP client:

    initial greeting, idle, envelop-from-sent, sending-envelop-to,
    prep-sending-data, sending-data, post-sending-data, final

    with the following transitions (illustrative, you should yse the
    RFC to ensure you have a conforming state diagram; some timeout ought
    to be added, disconnected events should be processed, and some more
    error checking too):

    initial state event action consequent state
    - connected - initial
    initial ok send HELO greeting
    initial error send QUIT;discon. final
    greeting ok - idle
    greeting error send QUIT;discon. final
    idle [got a msg to send] send MAIL FROM envelop-from-sent
    idle [no more msgt to send] send QUIT final
    envelop-from-sent ok send RCPT TO sending-envelop-to
    envelop-from-sent error send QUIT;discon. final
    sending-envelop-to ok [still got to] send RCPT TO sending-envelop-to
    sending-envelop-to ok [no more to] send DATA prep-sending-data
    sending-envelop-to error [still got to] send RCPT TO sending-envelop-to
    sending-envelop-to error [no more to] send DATA prep-sending-data
    prep-sending-data ok send first line sending-data
    prep-sending-data error send QUIT;discon. final
    sending-data line sent[still to go] send next line sending-data
    sending-data line sent[no more] send end-of-message post-sending-data
    post-sending-data ok - idle(msg sent!)
    post-sending-data error - idle(msg NOT sent)



    You could encode the states in such a program (and not taking into
    account the disconnect events: exercice for the reader):


    /* pseudo-code! */
    void smtp_client()
    {
    /* state=initial */
    receive_response(rep);
    if(ok_response(rep)){
    send("HELO");
    }else{
    send("QUIT");
    goto final;
    }
    /* state=greeting */
    receive_response(rep);
    if(!ok_response(rep)){
    send("QUIT");
    goto final;
    }
    while(got_a_message_to_send(message)){
    /* state=idle */
    send("MAIL FROM",message_mail_from(message));
    /* state=envelop-from-sent */
    receive_response(rep);
    if(!ok_response(rep)){
    send("QUIT");
    goto final;
    }
    /* state=sending-envelop-to */
    {
    int accepted_recipients=0;
    while(got_a_recipient(message)){
    send("RCPT TO",next_recipient(message));
    receive_reponse(rep);
    if(ok_response(rep)){
    accepted_recipients++;
    }
    }
    if(accepted_recipients<=0){
    message_not_sent(message);
    continue;
    }
    }
    /* state=prep-data */
    send("DATA");
    receive_response(rep);
    if(!ok_response(rep)){
    send("QUIT");
    goto final;
    }
    while(got_a_line(message)){
    /* state=sending-data */
    send_next_line(message);
    }
    send_end_of_message();
    /* state=post-sending-data */
    receive_response(rep);
    if(ok_response(rep)){
    message_sent(message);
    }else{
    message_not_sent(message);
    }
    }
    send("QUIT");
    final:
    /* state=final */
    disconnect();
    return;
    }


    So you don't have really a "loop". There are loops, but as determined
    by the state diagram. The advantage of such an implementation is that
    for simple protocols, the state is easy to follow: there's a
    correspondance to the state of the program. An inconvenient is that
    it does not scale well with bigger state diagrams, and it can lead to
    repeatitions (hence harder to maintain). And parts of the state are
    easier to maintain in variables anyway (for example:
    accepted_recipients).



    So you can store the state in a variable, and dispatch the processing
    depending on the current state:


    typedef enum { s_initial, s_greeting, s_idle, s_envelop_from_sent,
    s_sending_envelop_to, s_prep_sending_data,
    s_sending_data, s_post_sending_data, s_final } state_t;

    /* pseudo-code! */
    void smtp_client()
    {
    int accepted_recipients=0;
    state_t state=s_initial;
    while(1){
    switch(state){
    case s_initial:
    receive_response(rep);
    if(ok_response(rep)){
    send("HELO");
    state=s_greeting;
    }else{
    send("QUIT");
    state=s_final;
    }
    break;
    case s_greeting:
    receive_response(rep);
    if(ok_response(rep)){
    state=s_idle;
    }else{
    send("QUIT");
    state=s_final;
    }
    break;
    case s_idle:
    if(got_a_message_to_send(message)){
    send("MAIL FROM",message_mail_from(message));
    state=s_envelop_from_sent;
    }else{
    send("QUIT");
    state=s_final;
    }
    break;
    case s_envelop_from_sent:
    receive_response(rep);
    if(ok_response(rep)){
    accepted_recipients=0;
    state=s_sending_envelop_to;
    }else{
    send("QUIT");
    state=s_final;
    }
    break;
    case s_sending_envelop_to:
    if(got_a_recipient(message)){
    send("RCPT TO",next_recipient(message));
    receive_reponse(rep);
    if(ok_response(rep)){
    accepted_recipients++;
    }
    }else if(accepted_recipients<=0){
    message_not_sent(message);
    state=s_idle;
    }else{
    state=s_prep_sending_data;
    }
    break;
    case s_prep_sending_data:
    send("DATA");
    receive_response(rep);
    if(ok_response(rep)){
    state=s_sending_data;
    }else{
    send("QUIT");
    state=s_final;
    }
    break;
    case s_sending_data:
    if(got_a_line(message)){
    send_next_line(message);
    }else{
    send_end_of_message();
    state=s_post_sending_data;
    }
    break;
    case s_post_sending_data:
    receive_response(rep);
    if(ok_response(rep)){
    message_sent(message);
    }else{
    message_not_sent(message);
    }
    state=s_idle;
    break;
    case s_final:
    disconnect();
    return;
    default:
    panic("Internal error: Unknown state");
    break;
    }
    }
    }



    Note that I've simplified a lot the protocol (state diagram). For
    example, RFC821 distinguishes at least three outcomes for most
    commands: error, success and failure, where I just distinguished ok
    and error. With more states and more events, it would be interesting
    to put everything in a table, and have a general state machine
    processor.

    --
    __Pascal_Bourguignon__ http://www.informatimago.com/
    There is no worse tyranny than to force a man to pay for what he doesn't
    want merely because you think it would be good for him.--Robert Heinlein
    http://www.theadvocates.org/
    Pascal Guest

Similar Threads

  1. required modules in OS X; irb; socket.bundle
    By Gary.Palmer@ccmail.nevada.edu in forum Ruby
    Replies: 0
    Last Post: October 10th, 09:56 PM
  2. Replies: 1
    Last Post: September 11th, 01:41 PM
  3. Replies: 8
    Last Post: August 25th, 03:47 PM
  4. Distinguishing between socket buffer full & socket disconnected
    By John Ramsden in forum PERL Miscellaneous
    Replies: 1
    Last Post: August 5th, 11:01 AM

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139