Sockets


Python.

This module contains challenges for learning network programming in Python using the 'socket' library.


Challenges

Python Network Programming

Programming is fun, but sometimes we like to take it to the next level. That's where network programming comes in. Network programming in Python allows us to create programs that communicate with other programs on other computers. This could be a different computer on the same network, or even a computer on the internet. With network programming you can create a chat program that you and your friends can use or a web server to host your own website. The only limit is your creativity.

Sockets

In order to allow our program to connect to and interact to other computers we need to use sockets. You can think of sockets as walkie-talkies. Your friend has one walkie-talkie set to a particular channel. He's waiting for you to switch your walkie-talkie to that channel and initate a conversation.

Client and Server

When you get into the realm of network programming you will need to know the vocabulary of client and server.

  • client - A user seeking to connect to some resource on a network.
  • server - A resource being hosted on a network for others to connect to.

A great way to visualize this is with web browsing. Google hosts there search engine on a server as a resource. You, as a client, are able to connect to google and use their search engine.

Python Sockets

Now that we have a cursory understanding of what sockets are, we are ready to look at HOW we actually write a python program that puts these ideas into practice. The first thing we need is a library that interacts with our Operating Systems socket functionality. Thankfully, Python comes with its very own socket library, so no need to install anything extra! This can be used in our program as such:

import socket

Now, to create a socket we need to make an instance of the socket class that is inside the socket library we just imported:

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

In the above example, you may notice there are two weird parameters we are passing as we create a socket.

  • socket.AF_INET - This parameter identifies the 'address family' our socket will be using. The available 'address families' are AF_INET, which tells us we will be using IPv4, AF_INET6, which tells us we will be using IPv6, and AF_UNSPEC, which uses whatever is available.
  • socket.SOCK_STREAM - This parameter identifies which protocol we will be using between TCP and UDP. SOCK_STREAM says we will be using TCP, SOCK_DGRAM says we will be using UDP.

These two flags will be what you will use in 99% of your network programming, unless you specifically need to use a different address family or protocol.

After you create a socket, you have to decide whether you are creating a client or a server program. Right now, we will be creating a client program. Now that we have decided that, our path forward is relatively simple. All we must do now is connect our created socket to a resource we want access to, and then interact with it by sending and receiving data. Here is how to do that:

resource_address = ('IP', PORT_NUMBER)   # Notice, this is a tuple.
sock.connect(resource_address)

sock.send('DATA'.encode())
dirtuh = sock.recv(SIZE_IN_BYTES)

print(dirtuh.decode('UTF-8'))
sock.close()

In this challenge, using python, create a socket and connect it to '127.0.0.1' on port 1337. Send any data you want, and make sure to receive data from the server to get the flag!

Python Network Programming

In the last challenge we gave a high level overview of sockets. We mentioned the idea of client and server sockets. Clients connect to a particular resource over a network connection, and Servers are computers that are hosting these resources that others connect to.

In this challenge, we are going to look at how we can create our own Server sockets. This means we will be able to serve our own resource or service that lets others connect.

You will start off with the basic program from last challenge:

import socket

bind_addr = ('127.0.0.1', 1337)		# Note: This is a tuple
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

In the code above you can see we import the socket library. After that, we define the address and port we want to bind our socket to. To bind a socket is to in effect give it home address. We are saying, "Our service or resource can be found at '127.0.0.1' and port 1337." We then create our socket, calling it sock. Can you remember what socket.AF_INET and socket.SOCK_STREAM denote?

Note: The IP '127.0.0.1' denotes your own computer. It's also known as "localhost". This means that only YOU will be able to access whatever is being served on port 1337. It is not publically hosted.

From here, our steps diverge slightly. Rather than connecting our socket to a resource, we have to actually go through and bind it. Then we have to listen for new connections, and then we must accept the incoming connections as they come. The following code outlines what that looks like:

# Bind the socket to our desired address and port

sock.bind(bind_addr)	# Another way to write this without a variable would be 'sock.bind(('127.0.0.1', 1337))'

Listen for incoming connections. The paramater to the 'listen' method for our socket denotes the backlog. If backlog is specified, it must be at least 0 (if it is lower, it is set to 0); it specifies the number of unaccepted connections that the system will allow before refusing new connections. If not specified, a default reasonable value is chosen.

sock.listen(1)

Accept any incoming connections. We provide two varibles 'client_socket' and 'client_address' because the 'accept' method returns two variables. 'client_socket' will be an actual socket we can 'send' and 'recv' data from. 'client_address' is information about that socket.

client_socket, client_address = sock.accept()
# Now that we have a connected client, we can interact with it.
client_socket.send(DATA)
client_data = client_socket.recv(SIZE_IN_BYTES)

client_socket.close()

In this challenge, using python, create a server socket and bind it to '127.0.0.1' on port 1337. Be ready to receive connections. Run /challenge/run, and be ready to receive data from the connection that it makes. If you are successful, you should have the flag sent to you. Print out the data to get the flag!

Python Network Programming

In the last challenge, you created a server that you could connect to. However, once you connect to that server and close the connection, the server program itself finishes execution and ends. You won't be able to connect back to it unless you start the server again.

The way to keep the server running is to implement a server loop. This is a loop that keeps going until someone tells it to stop. This allows the server to accept connections even after other ones have been closed.

Think of websites. You are able to make a request to facebook, and then later that same day make another request to facebook. This is because the web server doesn't just end when clients are done, it stays open for potential new connections!

We can implement this in our code by writing a while loop (you can just use while True) around our sock.accept() call. Then, within the while loop, we can perform our necessary tasks with the connected socket before closing the connection.

Hint: This StackOverflow Article may prove helpful.

In this challenge, edit your server code so that it doesn't stop executing after one connection, but stays running and ready to accept new connections. When you're done, start your server, and run /challenge/run to test if it works. Remember that the client will be connecting to port 1337!

Keep in mind, for each new connection you get, you will need to receive data coming from the client and print it out.

Eight Ball Client

Now that you are familiar with sockets, we want to throw a challenge at you. There is a service we have created that functions as a magic 8 ball. Your job will be to create a client program that interacts appropriately with this service. I will outline how the 8 Ball server functions, and you will have to utilize that information to create an appropriate client.

Program Outline

Once you connect to the server at 127.0.0.1 on port 1337 the client will...

  1. Send you a banner so you know what you connected to.
  2. Receive a question from the client.
  3. It will then send an answer back and close the connection.

Note: If you ask it for the flag, it might give it to you.

Run /challenge/run to start the server. Then interact with it using your client program and try to get the flag!

Helpful Tools

When creating socket programs, you may get an error that looks like this:

``

This is really annoying, especially if you have to run tests rapidly, one after another. To fix this issue, we can call a particular method within our socket that allows us to configure options. This is the setsockopt method, which stands for 'set socket option'.

The function takes three paramters:

  1. Level
  2. Option
  3. Value

Level

This signifies the 'level' at which we are defining our selected option. The three most common levels are:

  • socket.SOL_SOCKET - Socket-level options (base level)
  • socket.IPPROTO_TCP - TCP-level options
  • socket.IPPROTO_UDP - UDP-level options

For most of your socket programming, setting options at the base level will be your go to.

Option

This signifies the actual 'option' you want to edit. The most common options are:

  • SO_BROADCAST - Enable or disable broadcasting on the socket.
  • SO_KEEPALIVE - Enable keepalive messages on the socket.
  • SO_REUSEADDR - Allow the socket to reuse a local address that is still in TIME_WAIT state after a previous connection has been closed.
  • SO_SNDBUF - Sets the size of the send buffer.
  • SO_RCVBUF - Sets the size of the receive buffer.

Value

The value you pass is dependant on the option you are setting. For SO_BROADCAST, SO_KEEPALIVE, and SO_REUSEADDR you will pass either a 1 or a 0.

  • 1 enables the option.
  • 0 disables the option.

For any other option, you will need to look up what value needs to be passed in order to configure it properly.

Now that you know that, I want you to create a simple server program that allows it to reuse its address and port that it is binding to. When you're done you will run '/challenge/run <your_filename>' so I can analyze your code and see if you did it correctly!


30-Day Scoreboard:

This scoreboard reflects solves for challenges in this module after the module launched in this dojo.

Rank Hacker Badges Score