Repository: pydantic/pydantic

Issue:
Url type port limit not checked
```
pydantic version: 1.5.1
pydantic compiled: False
install path: /opt/fastapi/backend/venv/lib/python3.8/site-packages/pydantic
python version: 3.8.3 (default, Jun 21 2020, 00:03:19)  [GCC 8.3.0]
platform: Linux-4.19.0-9-amd64-x86_64-with-glibc2.28
optional deps. installed: ['email-validator']
```

Hi,

The port limit is not checked by URLs types:

```py
>>> from pydantic import BaseModel, stricturl
>>> class Test(BaseModel):
	url: stricturl(allowed_schemes=["tcp"])

	
>>> Test(url="tcp://127.0.0.1:100000000000000000000000000000")
Test(url=UrlValue('tcp://127.0.0.1:100000000000000000000000000000', scheme='tcp', host='127.0.0.1', host_type='ipv4', port='100000000000000000000000000000'))
```
Excepted result: raise an error,

my current workaround
```py
>>> from pydantic import BaseModel, stricturl, validator
>>> from pydantic.networks import url_regex
>>> class Test(BaseModel):
	url: stricturl(allowed_schemes=["tcp"])
	@validator("url")
	def validate_port(cls, v):
		port = url_regex().match(v).group("port")
		if port is None:
		    return v

		if int(port) > 2**16:
			raise ValueError("port overflow")
		return v

	
>>> Test(url="tcp://127.0.0.1:900000")
Traceback (most recent call last):
  File "<pyshell#84>", line 1, in <module>
    Test(url="tcp://127.0.0.1:900000")
  File "pydantic\main.py", line 338, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for Test
url
  port overflow (type=value_error)
```




Discussion:
Is there a nice RFC explicitly stating a limit for port? If so I guess we could add a check, but this is not a bug, just a limit of the current validation.

You can implement a much cleaner validator:

```py
class Test(BaseModel):
    url: stricturl(allowed_schemes=['tcp'])

    @validator('url')
    def validate_port(cls, v):
        if int(v.port) > 2**16:
            raise ValueError('port overflow')
        return v
```
Hi,
[rfc 793](https://tools.ietf.org/html/rfc793#section-3.1) explain ports are 16 unsigned bits

Your validator will fail if port is missing, with tcp protocol that doesn't really make sense but with http(s) protocol is does:
example

`http://example.com` the validator will fail because v.port is None, int will raise a TypeError, but thanks for the tips I totally forgot port attribute 🤦


```py
class Test(BaseModel):
    url: stricturl(allowed_schemes=['tcp'])

    @validator('url')
    def validate_port(cls, v):
        if v.port is None:
            return v
        elif int(v.port) > 2**16:
            raise ValueError('port overflow')
        return v
```
(the tcp protocol is a bad showcase about default port)
Good point, `if v.port is not None and int(v.port) > 2**16` would be better. (is `port=0` allowed?)

> rfc 793 explain ports are 16 unsigned bits

PR welcome to add this check to all URL validation, I know technically it's a breaking chance, but I think a reasonable one, given that a higher port is invalid anyway.
the port 0 is tagged as reserved and should not be usable as such since most routers will reject it.
In a other hand in a lot of APIs port 0 mean "use the first avaiable socket" so I would say yes.

I'll work on a PR within a few days (this weekend I hope if I have time)
makes sense to allow 0, also makes the check easier.

> I'll work on a PR within a few days (this weekend I hope if I have time)

Thanks so much
I'm currently working on a PR,
looking at the sources of networks I have few questions about some design choices

Should I open news issues for each or asking here is fine ?


Resolving patch:
diff --git a/pydantic/errors.py b/pydantic/errors.py
--- a/pydantic/errors.py
+++ b/pydantic/errors.py
@@ -27,6 +27,7 @@
     'UrlUserInfoError',
     'UrlHostError',
     'UrlHostTldError',
+    'UrlPortError',
     'UrlExtraError',
     'EnumError',
     'IntegerError',
@@ -205,6 +206,11 @@ class UrlHostTldError(UrlError):
     msg_template = 'URL host invalid, top level domain required'
 
 
+class UrlPortError(UrlError):
+    code = 'url.port'
+    msg_template = 'URL port invalid, port cannot exceed 65535'
+
+
 class UrlExtraError(UrlError):
     code = 'url.extra'
     msg_template = 'URL invalid, extra characters found after valid URL: {extra!r}'
diff --git a/pydantic/networks.py b/pydantic/networks.py
--- a/pydantic/networks.py
+++ b/pydantic/networks.py
@@ -185,9 +185,14 @@ def validate(cls, value: Any, field: 'ModelField', config: 'BaseConfig') -> 'Any
         scheme = parts['scheme']
         if scheme is None:
             raise errors.UrlSchemeError()
+
         if cls.allowed_schemes and scheme.lower() not in cls.allowed_schemes:
             raise errors.UrlSchemePermittedError(cls.allowed_schemes)
 
+        port = parts['port']
+        if port is not None and int(port) > 65_535:
+            raise errors.UrlPortError()
+
         user = parts['user']
         if cls.user_required and user is None:
             raise errors.UrlUserInfoError()
@@ -205,7 +210,7 @@ def validate(cls, value: Any, field: 'ModelField', config: 'BaseConfig') -> 'Any
             host=host,
             tld=tld,
             host_type=host_type,
-            port=parts['port'],
+            port=port,
             path=parts['path'],
             query=parts['query'],
             fragment=parts['fragment'],


Test patch:
diff --git a/tests/test_networks.py b/tests/test_networks.py
--- a/tests/test_networks.py
+++ b/tests/test_networks.py
@@ -94,6 +94,7 @@ class Model(BaseModel):
             {'extra': ':db8::ff00:42:8329'},
         ),
         ('http://[192.168.1.1]:8329', 'value_error.url.host', 'URL host invalid', None),
+        ('http://example.com:99999', 'value_error.url.port', 'URL port invalid, port cannot exceed 65535', None),
     ],
 )
 def test_any_url_invalid(value, err_type, err_msg, err_ctx):