# Secure Oldies II: Refactoring and Make it Compile on macOS and Linux.

This second article is an continuation of my first article about building a simple HTTP client which runs on Windows 2000 onwards. 

Before going *backward* to the like of Windows 9x, Me, and NT 4.0 we're going lateral first. We'd try to make our code works on latest MacOS and Linux.

# The Tale of Portability

A *portable code* is a code that can be compiled and run on many different platforms. For now, our code only runs on Windows only. Specifically Windows NT 5.0 onwards. As I've said on my first article on this series, Sockets API is everywhere including macOS and Linux. So let's make it happen.

Windows implementations of Winsock is largely based on BSD socket. However, there are some difference between it and BSD socket usually used in Unix or Unix-like operating systems like Linux and macOS.

1. The most visible is that the existence of `WSAStartup()` and `WSACleanup()` function which is mandatory on Windows.
2. Windows uses `closesocket()` to close a socket rather than typical `close()` function available on POSIX compatible operating systems.
3. `SOCKET` on Windows is not file descriptor, it's private data structure specific to Windows and must be used by WinSock functions only and is not interchangeable with file descriptors with `int` data type.
4. `WSAGetLastError()` is used to get last error on Winsock code rather than `errno` typical in Unix systems.

Because of those differences, we'd need to isolate platform specific codes.

## Isolating Platform specific codes.

First of all we'd need to refactor out the header inclusion between Windows and Unix Headers. For that I'd create a file with `#ifdef` for each platform. I'll name it `platform.h`. The contents of this header is only platform specific header inclusion.

```c
#ifndef OTLS_PLATFORM_H
#define OTLS_PLATFORM_H 1 

#if defined (WIN32)
#define WIN32_LEAN_AND_MEAN // (1)
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#elif defined(__linux__) || defined (__unix__) || defined (__APPLE__) 
#define closesocket close // (2)
#define SOCKET int
#define INVALID_SOCKET -1
#define SOCKET_ERROR -1
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#else 
#error "Unsupported platform" 
#endif

// .. more code here ..

#endif
```

We add needed headers and add `#ifdef` for WIN32 :

1. This part will be compiled if we're targeting Windows
2. This part will be compiled if we're targeting Unix/Linux/macOS
    - We redefine `SOCKET` to `int` because in Unix, socket is file descriptor.
    - `INVALID_SOCKET` and `SOCKET_ERROR` will be `-1` as this is  the value on POSIX API for failure.
    - `<errno.h>` provides `errno` variable which similar to `WSAGetLastError()`.
    - `<netdb.h>` provides `struct in_addr` and function `gethostbyname()`.
    - `<netinet/in.h>` provides `struct sockaddr_in` and constants for internet protocol families.
    - `<arpa/inet.h>` provides `inet_ntoa` function which we're using to convert an address to human-readable address.
    - `<unistd.h>` provides `close()` function.


Also we're defining startup, cleanup and error code function which will call `WSAStartup`, `WSACleanup` in Windows and will be no-op in Unix. And will call `WSAGetLastError` in Windows and return `errno` in Unix.

```c
static inline int init_socket() {
#ifdef WIN32
  WSADATA wsa;
  return WSAStartup(MAKEWORD(2, 0), &wsa);
#else 
  return 0;
#endif
}

static inline int cleanup_socket() {
#ifdef WIN32
  WSADATA wsa;
  return WSAStartup(MAKEWORD(2, 0), &wsa);
#else 
  return 0;
#endif

}

static inline int get_last_socket_error() {
#ifdef WIN32
  return WSAGetLastError(); 
#else
  return errno;
#endif
}

```
With code isolated and some redefined to fit the target platform, we can just replace any occurence of `WSAStartup` to `init_socket()` `WSACleanup` to `cleanup_socket()` and `WSAGetLastError` to `get_last_socket_error()`. And the include part will be like so:

```c
#include <stdio.h>
#include <stdint.h>
#include <string.h>

#include "config.h"
#include "platform.h" // include this 

// ... more code below 
```
And also, you may see there's new other header `config.h`. I move every configuration to this file like MTU and maximum buffer size.

```c
#ifndef OTLS_CONFIG_H 
#define OTLS_CONFIG_H 1 

#define MTU 1500 
#define BUF_MAX_SIZE (8 * 2014)

#endif /* OTLS_CONFIG_H */
```

With this sorted out, all we need to do is just revise `CMakeLists.txt` a little bit so it only link to `ws2_32.dll` on Windows and not doing anything else on Linux or macOS.

```cmake
if (WIN32)
  target_link_libraries(binfetch ws2_32)
endif (WIN32)

```

## Rebuild and Run 

The step on macOS and linux is the same. Prepare a directory for building and then use these commands.

```sh
cd build

cmake ..

make

./binfetch
```

The command is short because I believe you're building the software natively. Because the code becomes complex. I've prepared the source repo for you to try.

%[https://github.com/retrocodingnet/otls]

Here's the result on macOS


![macos.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1652283499914/74vJS7Jem.gif align="left")

