|
Home > Archive > Unix Programming > February 2004 > Little C socket help required
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
Little C socket help required
|
|
| Materialised 2004-02-15, 5:35 am |
| 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
| |
| Pascal Bourguignon 2004-02-15, 7:34 am |
| Materialised <materialised@privacy.net> writes:
> 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
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/
| |
| Materialised 2004-02-15, 2:33 pm |
| Pascal Bourguignon wrote:
> Materialised <materialised@privacy.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]!=' ');
>
>
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:victim@here.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:mick@codegurus.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
| |
| Pascal Bourguignon 2004-02-15, 6:33 pm |
| Materialised <materialised@privacy.net> writes:
> Pascal Bourguignon wrote:
> 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:
So, you will try to receive 99 packets from the server before sending
Helo. Is that what the SMTP RFC specified???
> strcpy(data, "Helo\n");
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!
> 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:victim@here.com\n");
> len = strlen(data);
> if(sendall(sockfd, data, &len) == -1) {
> perror("sendall");
> fprintf(stderr, "We only send %d bytes because of the error");
> }
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.
> break;
> case 300:
> strcpy(data, "rcpt to:mick@codegurus.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) {
See below about packets/stream.
> perror("recv");
> exit(1);
> }
And??? Why don't you parse the response from the server? Don't you
think it may be interesting to know whether the server agrees to work
with you?
> 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 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).
> if( n == -1) {
> break;
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.
> }
> total += n;
> bytesleft -= n;
> }
>
> *len = total; // return number actually sent here
> return n==-1?-1:0; // return -1 on failure and 0 on sucess
> }
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/
| |
|
| Hi,
read about the select call. You can wait for a response on your file
descriptor.
Johan
"Materialised" <materialised@privacy.net> schreef in bericht
news:c0ocp5$18ira5$1@ID-220437.news.uni-berlin.de...
> 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
| |
| Josef newsgroup user 2004-02-16, 2:38 am |
| Johan wrote:
> =
> Hi,
> =
> read about the select call. You can wait for a response on your file
> descriptor.
> =
> Johan
> =
[color=blue]
> "Materialised" <materialised@privacy.net> schreef in bericht
> news:c0ocp5$18ira5$1@ID-220437.news.uni-berlin.de...
mtp[color=blue]
[color=blue]
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 =3D strtol(data);
return code < 400;
}
sd =3D 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:<Smith@USC-ISIF.ARPA>\r\n", ...))
exit(2);
if (!send_data(sd, "RCPT TO:<Jones@BBN-UNIX.ARPA>\r\n", ...))
exit(3);
if (!send_data(sd, "RCPT TO:<Green@BBN-UNIX.ARPA>\r\n", ...))
exit(4);
if (!send_data(sd, "RCPT TO:<Brown@BBN-UNIX.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=F6llers (Pinguinpfleger bei FSC)
If failure had no penalty success would not be a prize
-- T. Pratchett
| |
| Pascal Bourguignon 2004-02-16, 3:34 am |
| Materialised <materialised@privacy.net> writes:
> 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
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/artifa...hineDiagram.htm
The RFC informally documents 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 analyse 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/
|
|
|
|
|