Port forwarding in python to allow socket connections - python

I start a server using sockets and want to allow clients to connect to it.
self.sock.bind(('0.0.0.0',0)) # 0.0.0.0 will allow all connections and port 0 -> os chooses a open port.
stroke_port=self.sock.getsockname()[1]
self.sock.listen(75)
self.open_port_popup(stroke_port)
Now, for other clients to connect I have port forward it manually and it works fine.
I want to do this in automated fashion. -> I try upnp.
import miniupnpc
def open_port(port_no):
'''this function opens a port using upnp'''
upnp = miniupnpc.UPnP()
upnp.discoverdelay = 10
upnp.discover()
upnp.selectigd()
# addportmapping(external-port, protocol, internal-host, internal-port, description, remote-host)
result=upnp.addportmapping(port_no, 'TCP', upnp.lanaddr, port_no, 'testing', '')
return result
It opens a port shown in the image below. But the port-forwarding list shown in the first image is empty. It doesn't work and clients can't connect. How can I fix this? What am I missing?

I think you made a mistake using upnp.lanaddr as internal-host address. upnp.lanaddr is the address of the upnp device which is your router, you want to use the local address of your server.
If needed take a look at Finding local IP addresses using Python's stdlib if you want to get your server local IP dynamically.

I think that we are missing lot of related info to know what's the main problem here. I see so many people guessing.
By the way, just editing that line
result=upnp.addportmapping(port_no, 'TCP', upnp.lanaddr, port_no, 'testing', '') to
result=upnp.addportmapping('7777', 'TCP', '192.168.1.8', '7777', 'testing', '') would tell you if it works at all.
Doing port testing from localhost it's dummy, you're not under the router at all.
Also, remember to use Try/Except blocks to tell you what's wrong on your code.
try:
print "1" + 1
except Exception as e:
print str(e)
Another way, not fashioned is to use html/web automation, even cURL to make those requests instead using uPnp, this way you don't really need to handle it.

Most of the time ISP don't allow port forwarding, and you spend hours on this trying to forward port.
I went for ngrok - it's a lightweight free of cost (for basic usage) program that tunnels the port and give its own tunneled domain which can be accessed everywhere.

this is interesting question.
from what I could summon I think
GUI shows that UPNP port forwarding rules are added.
so Most likely there is issue in UPNPC configuration.
I doubt you are doing this on Router or similar platform with X-WRT or OpenWRT
the issue I think is you can't use upnp for this or it doesn't work for some strange reason.
I suggest you try this library pytables.
I know you wanted to know why and I am working on figuring out the reason.
this is just for you to get going on this project
and for quick solution
Try this
import subprocess
p = subprocess.Popen(["iptables", "-A", "INPUT", "-p", "tcp", "-m", "tcp", "--dport", "22" , "-j", "ACCEPT"], stdout=subprocess.PIPE)
output , err = p.communicate()
print output

Related

Get IPv4 Address from python, even with vpn on

So I'm using socket to connect clients to the server. For that, I need the computer's ip. Currently, the best way I found is this:
socket.gethostbyname(socket.gethostname())
I then use requests to tell my clients about my ip, and they connect. The issue here is that when my vpn is on, I get another host and that causes the clients to be unable to connect.
But when I open command prompt and type ipconfig, I get the correct ip regardless of the vpn status. So I need to get the same ip as would be shown under IPv4 in command prompt, is this possible in python?
I'm trying to get the server to work on any device regardless of exceptions such as this.
Thanks!
The way you retrieve the IP addresses of your system (most systems these days have multiple) uses the hostname of your system and therefore depends on i.e. DNS and your local hosts file. It will only give you one address, and can be quite unreliable, as you have seen with your VPN.
I'd recommend using the netifaces package. With it you can retrieve a list of all network interfaces and all their addresses.
An example from the manual:
>>> addrs = netifaces.ifaddresses('en0')
>>> addrs[netifaces.AF_INET]
[{'broadcast': '10.15.255.255', 'netmask': '255.240.0.0', 'addr': '10.0.1.4'}, {'broadcast': '192.168.0.255', 'addr': '192.168.0.47'}]
You should be able to install it with pip. The source repository is here: https://bitbucket.org/al45tair/netifaces
Yeah so I ran into this issue and it seems that ipconfig command does work, so I used the following. It calls ipconfig using subprocess and uses a regex pattern to match the ipv4 line. Since there's two ipv4 lines, and on my machine the VPN appeared as Unknown adapter WindscribeWireguard: before Wireless LAN adapter Wi-Fi: so I ended up matching the last one that appears. It's possible this isn't robust but I'm sure one of my users will let me know then.
from subprocess import check_output
import re
ipv4_pattern = re.compile(r'IPv4 Address.*:\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})')
def get_ipv4():
ipconfig_output = check_output(['ipconfig'], shell=True, text=True, encoding='iso8859-2')
return ipv4_pattern.findall(ipconfig_output)[-1]

Trouble initiating a TCP connection in Python--blocking and timing out

For a class project I'm trying to do some socket programming Python but running into a very basic issue. I can't create a TCP connection from my laptop to a lab machine. (Which I'm hoping to use as the "server") Without even getting into the scripts I have written, I've been simply trying interpreter line commands with no success. On the lab machine (kh4250-39.cselabs.umn.edu) I type the following into Python:
from socket import *
sock = socket()
sock.bind(('', 8353))
sock.listen(5)
sock.accept()
And then on my laptop I type:
from socket import *
sock = socket()
sock.connect(('kh4250-39.cselabs.umn.edu', 8353))
At which point both machines block and don't do anything until the client times out or I send a SIGINT. This code is pretty much exactly copied from examples I've found online and from Mark Lutz's book Programming Python (using '' for the server host name apparently uses the OS default and is fairly common). If I run both ends in my computer and use 'localhost' for the hostname it works fine, so I suspect it's some problem with the hostnames I'm using on one or both ends. I'm really not sure what could be going wrong on such a simple example. Does anyone have an idea?
A good way to confirm whether it's a firewall issue or not is to perform a telnet from the command-line to the destination host in question:
% telnet kh4250-39.cselabs.umn.edu 8353
Trying 128.101.38.44...
And then sometime later:
telnet: connect to address 128.101.38.44: Connection timed out
If it just hangs there at Trying and then eventually times out, chances are the connection to the remote host on that specific port is being blocked by a firewall. It could either be at the network layer (e.g. a real firewall or a router access-list) or at the host, such as iptables or other host-based filtering mechanisms.
Access to this lab host might only be available from within the lab or the campus network. Talk with your professor or a network administrator or someone "in the know" on the network to find out for sure.
Try to bind the server to 'kh4250-39.cselabs.umn.edu' instead of '':
sock.bind(('kh4250-39.cselabs.umn.edu', 8353))
If this does not work: Another reason could be a firewall blocking the port 8353....

Availability of IP Address - Python

How to check the availability of an IP address in python?
For example, I wan't to change my system's IP address to 192.168.112.226 statically overriding the dhcp provided address. The default gateway is 192.168.112.1. But I wan't to check before if anyone is using 192.168.112.226 before assigning to myself.
Usually do this in command line from bash. I check with ping 192.168.112.226. If host is unreachable, I use 'ifconfig' and 'route' to assign it to myself.
How to automate this using python?
PS: I prefer python so that I can use python-notify to beautify the output whether success or failure.
This is so bad in so many ways I can't even explain how awfull this is.
Why do you want this? Could you please tell us that, and we could come up with a much better answer than this utterly uggly "sollution"?
If you have a Linux/Unix system, you can make your DHCP client to request the DHCP-server to give you a specific IP address if the DHCP server know it's free. How to do this depends on the distribution.
There are two problems I see that you will create with your "sollution".
As some other has written, you could check to see that the IP is "free" right now, but the machine that own that IP address might start right after your test. Using its IP address, wich you have kidnapped.
If the DHCP server don't know that you have kidnapped an IP address, it could give it out to someone else.
Whatever it will break the network for that computer and yours, generating lots of work, and possible anger for/to the network administrator. And you don't want that, do you?
Okay, if you want to use bash, you can import os module or subprocess module.
for example:
import os
command = os.system('pint 192.168.112.226')
if command == 0: #Sucess
#write os.system() and give it ifconfig and route commands as parameter.
else: print "This IP is used by another person in your network."
you can read more about os.system and subprocess in python, by importing them and writing help(subprocess) for example.
You can use socket.gethostbyaddr() to find if IP Address is being in use or not.
import sys, os, socket
# Stores the IP Address
ip_address = sys.argv[1]
try:
socket.gethostbyaddr(ip_address)
# If previous line doesn't throw exception, IP address is being used by someone
print "No"
except socket.herror:
# socket.gethostbyaddr() throws error, so IP is not being used at present
# You can write os.system() and give it ifconfig and route commands as parameter.
print "Yes"
The problem with this code is that method.gethostbyaddr() takes lot of time to throw socket.herror if IP address is not in use on the network.
If you name this script as isIPAvailable.py, then it can be called by:
python isIPAvailable.py 192.168.112.226

Python: Open a Listening Port Behind a Router (upnp?)

I've developed an application that is essentially just a little ftp server with the ability to specify which directory you wish to share on startup. I'm using ftplib for the server because it's sick easy. The only issue I'm having is that if you are behind a router you have to manually forward the ports on your router and I'm finding that it's a little too complicated for my users (aka co-workers/clients).
So I've been looking for a simple solution to open ports but I'm finding that most APIs are too broad and way over my head. Does someone know of a solution that would be relatively simple to implement?
Note: It will really only be used on windows although cross-platform compatibility would be welcomed. If there is a windows only solution that is simpler then I would opt for that.
Thanks!
Simple example for miniupnp. It creates a mapping on the discovered gateway from external port 43210 to the interface connected to port 43210 on the interface connected to the discovered gateway.
import miniupnpc
upnp = miniupnpc.UPnP()
upnp.discoverdelay = 10
upnp.discover()
upnp.selectigd()
port = 43210
# addportmapping(external-port, protocol, internal-host, internal-port, description, remote-host)
upnp.addportmapping(port, 'TCP', upnp.lanaddr, port, 'testing', '')
The protocol you want is called IGD (for Internet Gateway Device) and is based on UPNP. It allows a client program (yours) to discover the router on the network (using UPNP) and then ask it to forward a specific port.
This is supported by most home routers, and the technique is used by a lot of services like BitTorrent or multiPlayer games, bit it's a bit complicated to use or implement. There are several open source libraries that support IGD and one of the simplest one (which is also cross-platform) is "miniupnp": see http://miniupnp.free.fr/
Looks like there are a few options, one being miniupnp. There are also python bindings for GNUPnP here. For windows minupnp will work, or you could go pure python with miranda-upnp.
There is a nice example of the python GNUPnP bindings being used to open ports on a router here.
In that example the lease time is set to 0, which is unlimited. See here for the definition of add_port.
A simple example might be:
#! /usr/bin/python
import gupnp.igd
import glib
from sys import stderr
my_ip = YOUR_IP
igd = gupnp.igd.Simple()
igd.external_ip = None
main = glib.MainLoop()
def mep(igd, proto, eip, erip, port, localip, lport, msg):
if port == 80:
igd.external_ip = eip
main.quit()
def emp(igd, err, proto, ep, lip, lp, msg):
print >> stderr, "ERR"
print >> stderr, err, proto, ep, lip, lp, msg
main.quit()
igd.connect("mapped-external-port", mep)
igd.connect("error-mapping-port", emp)
#igd.add_port("PROTO", EXTERNAL_PORT, INTERNAL_IP, INTERNAL_PORT, LEASE_DURATION_IN_SECONDS, "NAME")
igd.add_port("TCP", 80, my_ip, 8080, 86400, "web")
main.run()
There is an article explaining how to use the Windows IGD COM object with win32com.
I looked for this for many days. I was unable to install miniupnpc using pip for python 3.
I solved this isue with an implementation found here which will work for python 2.
I forked it and make the changes to be used on python 3, you can find it Here
This implementation is by far the simplest I have seen and works well.

Using pySerial to connect to a non-COM port

In Hyperterminal I am able to connect to a serial port called "X64-CL_iPro_1_Serial_0" where I am able to send/receive ASCII text to a camera. However when I try to connect to the same port with pySerial, it throws an exception:
SerialException: could not open port X64-CL_iPro_1_Serial_0: [Error 2] The system cannot find the file specified.
I don't understand why Hyperterminal can detect the port and communicate with it, but Python can't. I downloaded this script from the pySerial website that displays a list of serial ports, and the only ports it came up with was COM1 and COM2, neither of which I can connect to.
My code is very simple, and looks like this:
import serial
port = "X64-CL_iPro_1_Serial_0"
ser = serial.Serial(port)
Am I doing anything wrong? Is there a way to work around this? Thanks ahead of time.
Edit:
It should also be noted that this port does not show up in the device manager, and neither does COM1 or COM2.
The problem lies in the enumeration code you linked. It is wrong in two regards:
It uses a fixed GUID_CLASS_COMPORT to enumerate. It should instead ask the GUID through SetupDiClassGuidsFromName, passing "Ports" as description of the class for which it is asking for names.
The code insists of asking for the friendly name of the port. But if the only goal is to open the device (instead of displaying to an user), it should directly access the DevicePath element, which is a weird-looking-but-perfectly-valid port name to pass to pySerial. The friendly name might even be totally missing.
aside
I'm not clear the question is about non-serial-port use through pyserial, or serial port that is not a COMX port in enumeration.
This may be a bit OT or too hard code for your use, but here goes first, using some other file in a pyserial object:
foo = serial.Serial()
peer = serial.Serial()
foo.fd, peer.fd = posix.openpty()
try: foo._isOpen = peer._isOpen = True # depending on pyserial version
except: pass
foo._reconfigurePort()
peer.setTimeout(timeout=0.1)
peer._reconfigurePort()
Regarding second, remember that ports beyond COM9 use weird windows notation \\.\COM10, perhaps that's what Hyperterminal does for you. pyserial doesn't do it, so perhaps you need to open the port something like this:
s = serial.Serial("\\\\.\\X64-CL_iPro_1_Serial_0") # also remember to escape backslash

Categories