Source code for onbasca.base.models.bridge
# SPDX-FileCopyrightText: 2022 The Tor Project, Inc.
#
# SPDX-License-Identifier: BSD-3-Clause
"""Bridge base model."""
import logging
import re
from django.db import models
from onbasca.bridgeline import parse_bridge_line
from . import BaseManager, BaseModel
logger = logging.getLogger(__name__)
[docs]
def fingerprint_from_bridgeline(bridgeline):
"""
Obtain the bridge fingerprint from a bridge line.
From ``7. Displaying Bridge Information``
(https://gitlab.torproject.org/tpo/core/torspec/-/blob/main/bridgedb-spec.txt#L357)
Bridges are formatted as::
<address:port> NL
Pluggable transports are formatted as::
<transportname> SP <address:port> [SP arglist] NL
Example::
- with transport::
obfs4 <ip>:<port> <fingerprint> cert=<cert> iat-mode=<iat-mode>
- without transport::
<ip>:<port> <fingerprint>
"""
# Regular expression from:
# https://gitlab.torproject.org/tpo/anti-censorship/bridgestrap/-/blob/719bed120f6d01829d04c19e21a80fc9a04f393e/tor.go#L41 # noqa
fp_re = re.compile("([A-F0-9]{40})")
# Assume any line as ``(transport) <ip>:<port> (<fingerprint>)`` is valid.
fp = re.findall(fp_re, bridgeline)
if len(fp) == 1:
logger.debug("bridgeline contains fingerprint.")
return fp[0]
# Regular expression from:
# https://gitlab.torproject.org/tpo/anti-censorship/bridgestrap/-/blob/719bed120f6d01829d04c19e21a80fc9a04f393e/cache.go#L17 # noqa
ip_port_re = re.compile("[0-9a-z\[\]\.:]+:[0-9]{1,5}") # noqa:W605
ip_port = re.findall(ip_port_re, bridgeline)
if len(ip_port) == 1:
logger.warning("Not found fingerprint in bridgeline but found ip:port")
return ip_port[0]
logger.warning("No fingerprint nor ip found in bridgeline")
return None
[docs]
class BridgeManagerBase(BaseManager):
[docs]
def from_bridgeline(self, bridgeline):
"""Create a bridge from a bridgeline"""
logger.debug("Creating bridge from bridgeline %s", bridgeline)
bridgeline_kwargs = parse_bridge_line(bridgeline)
if not bridgeline_kwargs:
return "bridgeline is not valid.", False
# if there're several `kwargs`, then the fingerprint is in the 2nd
# position.
if (
len([kwarg for kwarg in bridgeline_kwargs if kwarg is not None])
> 1
):
fingerprint = bridgeline_kwargs[1]
# otherwise, use the addrport as the fingerprint, which is in the 1st
# position.
else:
fingerprint = bridgeline_kwargs[0]
bridge, created = self.update_or_create(
fingerprint=fingerprint,
defaults={"bridgeline": bridgeline},
)
logger.info("Bridge %s created %s", bridge, created)
return bridge, created
[docs]
class BridgeBase(BaseModel):
objects = BridgeManagerBase()
fingerprint = models.CharField(primary_key=True, max_length=40)
# What's the max and min length?, 154?
bridgeline = models.CharField(max_length=255, null=True, blank=True)
def __str__(self):
return "{}".format(self.fingerprint)