0

I'm trying to learn how to use SMTP on C using Winsock. I've followed the guide provided by Microsoft, and the final code as is, works perfectly. Now, I decided to take it a step further and make it a little bit more "organized" by taking different parts of the code and put them in functions with appropiate names. I was happy to see that my code compiled succesfully, with no errors at all, but after running it for the first time, the VSC Debugger notified me of a segmentation fault. The problematic line (so far) is this one:

89|  *iResult = connect( *ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);

Right now I'm kinda lost, since the way you acces pointer structs dosent "add up" like with pointers, where you can have a **var1. Maybe the error is not actually on the problematic line itself, but before... I dont know, I'm lost.

Here is my code so far:

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#define _WIN32_WINNT 0x0501

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <stdlib.h>

#define DEFAULT_PORT "25"

char data[500];

int initWinsock(int *, WSADATA *);
int makeSocket(int *, struct addrinfo *, struct addrinfo *, struct addrinfo *, SOCKET *);
int connSocket(int *, SOCKET *, struct addrinfo *, struct addrinfo *);
int clientReceive(int *, SOCKET *, char *);
int closeSocketListener(int *, SOCKET *);
int closeSocket(SOCKET *);

int main() {
    SOCKET ConnectSocket = INVALID_SOCKET;
    WSADATA wsaData;
    int iResult;
    struct addrinfo *result = NULL,
                    *ptr = NULL,
                    hints;
    ZeroMemory( &hints, sizeof(hints) );
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    
    initWinsock(&iResult, &wsaData);

    makeSocket(&iResult, &hints, result, ptr, &ConnectSocket);

    connSocket(&iResult, &ConnectSocket, ptr, result);
    
    clientReceive(&iResult, &ConnectSocket, data);

    closeSocketListener(&iResult, &ConnectSocket);

    closeSocket(&ConnectSocket);

    system("pause");
    return 0;
}

int initWinsock(int *iResult, WSADATA *wsaData){
    // Initialize Winsock
    *iResult = WSAStartup(MAKEWORD(2,2), wsaData);
    if (*iResult != 0) {
        printf("WSAStartup failed: %d\n", *iResult);
        return 1;
    }
    return 0;
}

int makeSocket(int *iResult, struct addrinfo *hints, struct addrinfo *result, struct addrinfo *ptr, SOCKET *ConnectSocket){
    // Resolve the server address and port
    *iResult = getaddrinfo("52.97.26.134", DEFAULT_PORT, hints, &result);
    if (*iResult != 0) {
        printf("getaddrinfo failed: %d\n", *iResult);
        WSACleanup();
        return 1;
    }

    // Attempt to connect to the first address returned by
    // the call to getaddrinfo
    ptr=result;

    // Create a SOCKET for connecting to server
    *ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);

    if (*ConnectSocket == INVALID_SOCKET) {
        printf("Error at socket(): %d\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return 1;
    }
    return 0;
}

int connSocket(int *iResult, SOCKET *ConnectSocket, struct addrinfo *ptr, struct addrinfo *result){
    // Connect to server.
    *iResult = connect( *ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
    if (*iResult == SOCKET_ERROR) {
        closesocket(*ConnectSocket);
        *ConnectSocket = INVALID_SOCKET;
    }

    // Should really try the next address returned by getaddrinfo
    // if the connect call failed
    // But for this simple example we just free the resources
    // returned by getaddrinfo and print an error message

    freeaddrinfo(result);

    if (*ConnectSocket == INVALID_SOCKET) {
        printf("Unable to connect to server!\n");
        WSACleanup();
        return 1;
    }
    return 0;
}

int clientReceive(int *iResult, SOCKET *ConnectSocket, char *out){
    *iResult = recv(*ConnectSocket, out, 200, 0);
    if (iResult > 0) {
        printf("Bytes received: %d\n", *iResult);
        return 0;
    } else if (iResult == 0) {
        printf("Connection closed\n");
        return 1;
    } else {
        printf("recv failed: %d\n", WSAGetLastError());
        return 2;
    }

    return -1;
}

int closeSocketListener(int *iResult, SOCKET *ConnectSocket){
    // shutdown the send half of the connection since no more data will be sent
    *iResult = shutdown(*ConnectSocket, SD_SEND);
    if (*iResult == SOCKET_ERROR) {
        printf("shutdown failed: %d\n", WSAGetLastError());
        closesocket(*ConnectSocket);
        WSACleanup();
        return 1;
    }
    return 0;
}

int closeSocket(SOCKET *ConnectSocket){
    // cleanup
    closesocket(*ConnectSocket);
    WSACleanup();
    return 0;
}
fpp
  • 31
  • 4
  • just a guess, at the `getaddrinfo` fourth parameter is actually address of local variable, maybe if you pass pointer to pointer instead – Kazz Feb 28 '23 at 07:07
  • also the arrow operator (the `->`) just dereference the variable before accessing its members, so this: `var->member` is the same thing as `(*var).member`, and you can have pointer to pointer to struct, so pointers does "add up" regardless the variable is pointer to basic type or struct, you just have to dereference it in order to access member, for example `struct structname **var1` you can access member like: `(*var1)->member` or `(**var1).member` – Kazz Feb 28 '23 at 07:45
  • Whan you call `connSocket(&iResult, &ConnectSocket, ptr, result)`, `ptr` is NULL, so what do you expect? Calling `makeSocket` does not modify `ptr`. – Jabberwocky Feb 28 '23 at 08:42
  • Read this: https://stackoverflow.com/questions/766893/how-do-i-modify-a-pointer-that-has-been-passed-into-a-function-in-c – Jabberwocky Feb 28 '23 at 08:50

1 Answers1

0

From withing the scope of function body all parameters are local variables. You can think of a parameters as a local variables, even tho they are not, obviously as you can assign value to them from "outside", which is not true for actual local variables.

Example 1):

void foo(int bar)
{
    bar = 123;
}

There is nothing weird, you would not expect that this function affect anything from outside of this function.

int var = 987;
foo(var);
printf("%i", var); // prints 987 as expected

But then there are pointers, example 2):

void foo(int *bar)
{
    *bar = 123;
}

Which does affect the outside value.

int var = 987;
foo(&var);
printf("%i", var); // prints 123, again as expected

What about this example 3):

void foo(int *bar)
{
    int baz = 123;
    bar = &baz;
}

Now when we call it:

int var = 987;
foo(&var);
printf("%i", var); // prints 987

How so ? Shouldn't this line bar = &baz; overwrite the value of bar ? And the answer is it should and it does, but bar is only "local variable" and that is why we cannot see the effect outside. The reason why is absolutely same as in the first example.

When we take a look at the second example where it actually does affect outside value, the only difference is dereference.

Only when you dereference a pointer you are reading from/writing to value outside the function body, please note that arrow operator is special type of dereference of pointer to struct.

Your relevant code:

int makeSocket(int *iResult, struct addrinfo *hints, struct addrinfo *result, struct addrinfo *ptr, SOCKET *ConnectSocket){
    *iResult = getaddrinfo("52.97.26.134", DEFAULT_PORT, hints, &result);
    // ...
    ptr=result;
    // ...
}

This line: ptr=result; is the same as example 3, no effect on the "outside" value.

The &result means address of local variable, lets imagine how is the value setted up withing function getaddrinfo:

void getaddrinfo(/* ... */ int **bar)
{
    static int baz = 123;
    *bar = &baz;
}

Now if we call that function:

int *var;
getaddrinfo(&var);
printf("%i", *var); // prints 123

But:

void foo(int *bar)
{
    getaddrinfo(&bar);
    printf("%i", *bar); // prints 123
}
int _default = 987;
int *var = &_default;
foo(var);
printf("%i", *var); // prints 987, as if the "foo(var);" wasn't called at all

Solution could be:

void foo(int **bar)
{
    getaddrinfo(bar);
}
int *var;
foo(&var);
printf("%i", *var); // prints 123
Kazz
  • 1,030
  • 8
  • 16
  • Thanks! The problem was actually carried from that `ptr=result` to the line 89. After reading all what you said I understood that the problem was that the variables were pointing to nothing and so it generates a segmentation error when passing the argument to `connect()`. I fixed the problem by making `result` and `ptr` pointers of pointers (`**`) and then passing the memory direction of the original pointer (in `main()`) as argument to `makeSocket()`. – fpp Feb 28 '23 at 17:39