diff options
author | B. Stack <bgstack15@gmail.com> | 2022-03-16 14:10:45 -0400 |
---|---|---|
committer | B. Stack <bgstack15@gmail.com> | 2022-03-16 14:10:45 -0400 |
commit | 5ff15f53eb16c1b6326c6127744908d9b105214c (patch) | |
tree | 654eeb5729f1f3be6022c1b3be2ea9480c3c3f16 /fifconfig.py | |
download | fifconfig-5ff15f53eb16c1b6326c6127744908d9b105214c.tar.gz fifconfig-5ff15f53eb16c1b6326c6127744908d9b105214c.tar.bz2 fifconfig-5ff15f53eb16c1b6326c6127744908d9b105214c.zip |
initial commit
Diffstat (limited to 'fifconfig.py')
-rw-r--r-- | fifconfig.py | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/fifconfig.py b/fifconfig.py new file mode 100644 index 0000000..6ff8d17 --- /dev/null +++ b/fifconfig.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python3 +# File: fifconfig.py +# Location: https://gitlab.com/bgstack15/fifconfig +# Author: bgstack15 +# SPDX-License-Identifier: GPL-3.0 +# Startdate: 2022-03-15 20:35 +# Title: Flask-ifconfig +# Purpose: Act similar to http://ifconfig.me but with flask, for my network testing usage +# History: +# Usage: +# Reference: +# https://tedboy.github.io/flask/generated/generated/flask.Request.html +# https://bgstack15.ddns.net/cgit/stackbin/tree/stackbin.py +# https://pypi.org/project/dicttoxml/ +# Improve: +# Dependencies: +# pip-devuan: json2html +# pip-centos7: json2html +# dep-devuan: python3-dicttoxml | python3-xmltodict +# dep-centos7: python36-xmltodict +# reverse-proxy configs that include X-Forwarded-Prefix headers. +# Documentation: see README.md + +from flask import Flask, request, jsonify, url_for + +app = Flask(__name__) +try: + app.config.from_pyfile('fifconfig.conf') +except: + pass + +def _make_dict_safe_for_text(response): + """ Mostly for converting the silly x-forwarded-for ImmutableList so that it does not show that class name. """ + # This works except for the silly "ImmutableList([" text output for that one 'via' item. + #response = { i:str(response[i]) for i in response } + # So instead of that beautiful oneliner, you get this stupid 9-line block. + gen = (i for i in response) + new_response = {} + for i in gen: + if "<class 'werkzeug.datastructures.ImmutableList'>" == str(type(response[i])): + #print(f"i {i} is a immutablelist") + new_response[i] = ','.join(response[i]) + else: + new_response[i] = str(response[i]) + return new_response + +def get_attribs( + request, + lia = False, + ia = False, + ua = False, + l = False, + re = False, + m = False, + e = False, + mt = False, + c = False, + xff = False +): + """ + Main function that builds the desired response dict + that will later be turned into json or html by the + various small @app.route functions. + """ + response = {} + r = request + rh = r.headers + if lia: + response['lastipaddress'] = r.remote_addr + if ia: + response['ipaddress'] = r.remote_addr + if r.access_route and len(r.access_route) > 0: + response['ipaddress'] = r.access_route[0] + if ua: + response['useragent'] = str(r.user_agent) + if l: + response['language'] = r.accept_languages + if re: + response['referer'] = r.referrer + if m: + response['method'] = r.method + if e: + response['encoding'] = r.accept_encodings + if mt: + response['mimetype'] = r.accept_mimetypes + if c: + response['charset'] = r.accept_charsets + if xff: + #response['x-forwarded-for'] = rh.get('X-Forwarded-For') or '' + response['x-forwarded-for'] = r.access_route + #'charset': rh.get('Accept-Charset') or '', + #'endpoint': request.endpoint + # via is the same as x-forwarded-for + #'via': r.access_route + print(f"DEBUG: dict is {response}") + return response + +def prepare_output(request, response): + """ + Used to customize the output to json, html, or text depending on what the client asked for. In order of most important to least important: + 1. request argument, i.e., '?json' + 2. Accept-Mimetypes header + """ + possible_formats = ['json','html','text','xml'] + _format = "text" + # priority two + for i in request.accept_mimetypes: + for j in i: + if 'application/xml' == j and _format != "html": + _format = "xml" + if 'application/json' == j: + _format = "json" + if 'application/xhtml+xml' == j or 'text/html' == j: + _format = "html" + if 'text/plain' == j: + _format = "text" + # priority one + if request.args: + if 'xml' in request.args: + _format = "xml" + if 'json' in request.args: + _format = "json" + if 'html' in request.args: + _format = "html" + if 'text' in request.args: + _format = "text" + if _format not in possible_formats: + print(f"DEBUG (prepare_output): how did format {_format} get defined?! Using text.") + _format = "text" + # main process + if "html" == _format: + print("Sending html") + if True: + from json2html import json2html + response = _make_dict_safe_for_text(response) + response = json2html.convert(json = response) + if not 'nolinks' in request.args: + prefix = '' + if 'HTTP_X_FORWARDED_PREFIX' in request.environ: + prefixes = request.environ['HTTP_X_FORWARDED_PREFIX'] + prefix = ''.join(prefixes.split(',')).replace(' ','') + response += f"<div style='font-size: 80%;'>" + response += f"<a href='{prefix}{url_for('ip')}'>ip</a> " + response += f"<a href='{prefix}{url_for('ua')}'>ua</a> " + response += f"<a href='{prefix}{url_for('lang')}'>lang</a> " + response += f"<a href='{prefix}{url_for('encoding')}'>encoding</a> " + response += f"<a href='{prefix}{url_for('mime')}'>mime</a> " + response += f"<a href='{prefix}{url_for('charset')}'>charset</a> " + response += f"<a href='{prefix}{url_for('forwarded')}'>forwarded</a> " + #response += f"<p>{request.environ}<p>" + response += f"<a href='{app.config['SOURCE_URL']}'>SOURCE</a>" + response += f"</div>" + return response + else: + print("Unable to load json2html, sending plain text.") + return str(response) + elif "xml" == _format: + print("Sending xml") + pretty = False + if request.args and 'pretty' in request.args: + pretty = True + try: + import xmltodict + response = xmltodict.unparse({'info':response}) + except: + try: + # This lib is objectively better but not available natively on CentOS 7 + print("Trying dicttoxml") + from dicttoxml import dicttoxml + response = dicttoxml(response,custom_root="info") + except: + print("Unable to load xmltodict, sending plain text.") + return str(response) + if pretty: + try: + from xml.dom.minidom import parseString + response = parseString(response).toprettyxml() + except: + pass + return response + elif "json" == _format: + print("Sending json") + return jsonify(response) + else: + # the only other option is text + print("Sending text") + response = _make_dict_safe_for_text(response) + new_response = "" + if 1 == len(response): + for i in response: + return response[i] + else: + for i in response: + new_response = new_response + '\n' + str(i) + ': ' + str(response[i]) + return new_response + +@app.route('/') +def root(): + response = get_attribs(request, lia = False, ia = True, ua = True, l = True, re = True, m = True, e = True, mt = True, c = True, xff = True) + response = prepare_output(request, response) + return response + +@app.route('/ip') +def ip(): + return prepare_output(request, get_attribs(request, ia = True)) + +@app.route('/ua') +def ua(): + return prepare_output(request, get_attribs(request, ua = True)) + +@app.route('/lang') +def lang(): + return prepare_output(request, get_attribs(request, l = True)) + +@app.route('/encoding') +def encoding(): + return prepare_output(request, get_attribs(request, e = True)) + +@app.route('/mime') +def mime(): + return prepare_output(request, get_attribs(request, mt = True)) + +@app.route('/charset') +def charset(): + return prepare_output(request, get_attribs(request, c = True)) + +@app.route('/forwarded') +def forwarded(): + return prepare_output(request, get_attribs(request, xff = True)) + +if __name__ == "__main__": + app.run() |