Skip to content

Commit

Permalink
Bug 1929192 - Remove Windows XP and Vista logic
Browse files Browse the repository at this point in the history
  • Loading branch information
willdurand committed Nov 8, 2024
1 parent 4f8ebcf commit bda4491
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 338 deletions.
206 changes: 32 additions & 174 deletions handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,26 @@ import (
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
"time"
"unicode"

"github.com/mozilla-services/go-bouncer/bouncer"
)

const (
DefaultLang = "en-US"
DefaultOS = "win"
firefoxSHA1ESRAliasSuffix = "sha1"
fxPre2024LastNightly = "firefox-nightly-pre2024"
fxPre2024LastBeta = "127.0b9"
fxPre2024LastRelease = "127.0"
esr115Product = "firefox-esr115-latest-ssl"
defaultLang = "en-US"
defaultOS = "win"
fxPre2024LastNightly = "firefox-nightly-pre2024"
fxPre2024LastBeta = "127.0b9"
fxPre2024LastRelease = "127.0"
esr115Product = "firefox-esr115-latest-ssl"
)

type xpRelease struct {
Version string
}

var (
// detects Windows XP and Vista clients
windowsXPRegex = regexp.MustCompile(`Windows (?:NT 5\.1|XP|NT 5\.2|NT 6\.0)`)
// detects windows 7/8/8.1 clients
windowsRegexForESR115 = regexp.MustCompile(`Windows (?:NT 6\.(1|2|3))`)
// this is used to verify the referer header
Expand All @@ -44,13 +39,6 @@ var (
win64Regex = regexp.MustCompile(`Win64|WOW64`)
)

var tBirdWinXPLastRelease = xpRelease{"38.5.0"}
var tBirdWinXPLastBeta = xpRelease{"43.0b1"}

func isWindowsXPUserAgent(userAgent string) bool {
return windowsXPRegex.MatchString(userAgent)
}

func isUserAgentOnlyCompatibleWithESR115(userAgent string) bool {
return windowsRegexForESR115.MatchString(userAgent)
}
Expand All @@ -63,133 +51,8 @@ func isWin64UserAgent(userAgent string) bool {
return win64Regex.MatchString(userAgent)
}

func isNotNumber(r rune) bool {
return !unicode.IsNumber(r)
}

// a < b = -1
// a == b = 0
// a > b = 1
func compareVersions(a, b string) int {
if a == b {
return 0
}
aParts := strings.Split(a, ".")
bParts := strings.Split(b, ".")

for i, verA := range aParts {
if len(bParts) <= i {
return 1
}
verB := bParts[i]

aInt, err := strconv.Atoi(strings.TrimRightFunc(verA, isNotNumber))
if err != nil {
aInt = 0
}
bInt, err := strconv.Atoi(strings.TrimRightFunc(verB, isNotNumber))
if err != nil {
bInt = 0
}

if aInt > bInt {
return 1
}
if aInt < bInt {
return -1
}
}
return 0
}

func tBirdSha1Product(productSuffix string) string {
switch productSuffix {
case "beta", "beta-latest":
return tBirdWinXPLastBeta.Version
case "ssl":
return tBirdWinXPLastRelease.Version + "-ssl"
case "latest":
return tBirdWinXPLastRelease.Version
}

productSuffixParts := strings.SplitN(productSuffix, "-", 2)
ver := productSuffixParts[0]

possibleVersion := tBirdWinXPLastRelease
if strings.Contains(ver, ".0b") {
possibleVersion = tBirdWinXPLastBeta
}

if compareVersions(ver, possibleVersion.Version) == -1 {
return productSuffix
}

if len(productSuffixParts) == 1 {
return possibleVersion.Version
}

if productSuffixParts[1] == "ssl" {
return possibleVersion.Version + "-ssl"
}

return productSuffix
}

func firefoxSha1Product(productSuffix string) string {
// Example list of products:
// Firefox-48.0-Complete
// Firefox-48.0build1-Complete
// Firefox-48.0
// Firefox-48.0-SSL
// Firefox-48.0-stub
// Firefox-48.0build1-Partial-47.0build3
// Firefox-48.0build1-Partial-47.0.1build1
// Firefox-48.0build1-Partial-48.0b10build1
// Firefox-48.0-Partial-47.0
// Firefox-48.0-Partial-47.0.1
// Firefox-48.0-Partial-48.0b10

// Example list of aliases:
// firefox-beta-latest
// firefox-beta-sha1
// Firefox-beta-stub
// firefox-esr-latest
// firefox-esr-sha1
// firefox-latest
// firefox-sha1
// Firefox-stub

// Do not touch products ending with "sha1"
if strings.HasSuffix(productSuffix, "-sha1") {
return productSuffix
}

// Do not touch completes and partials
if strings.HasSuffix(productSuffix, "-complete") || strings.Contains(productSuffix, "-partial-") {
return productSuffix
}
return firefoxSHA1ESRAliasSuffix
}

func sha1Product(product string) string {
productParts := strings.SplitN(product, "-", 2)
if len(productParts) == 1 {
return product
}

if productParts[0] == "firefox" {
return "firefox-" + firefoxSha1Product(productParts[1])
}

if productParts[0] == "thunderbird" {
return "thunderbird-" + tBirdSha1Product(productParts[1])
}

return product
}

// detect stub installers that pin the "DigiCert SHA2 Assured ID Code Signing CA" intermediate

// isPre2024StubUserAgent is used to detect stub installers that pin the
// "DigiCert SHA2 Assured ID Code Signing CA" intermediate.
func isPre2024StubUserAgent(userAgent string) bool {
return "NSIS InetBgDL (Mozilla)" == userAgent
}
Expand Down Expand Up @@ -272,7 +135,7 @@ func (h *HealthHandler) check() *HealthResult {
return result
}

func (h *HealthHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
func (h *HealthHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
if h.CacheTime > 0 {
w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", h.CacheTime/time.Second))
}
Expand All @@ -291,15 +154,15 @@ type BouncerHandler struct {
db *bouncer.DB

CacheTime time.Duration
PinHttpsHeaderName string
PinHTTPSHeaderName string
PinnedBaseURLHttp string
PinnedBaseURLHttps string
StubRootURL string
}

// URL returns the final redirect URL given a lang, os and product
// if the string is == "", no mirror or location was found
func (b *BouncerHandler) URL(pinHttps bool, lang, os, product string) (string, error) {
func (b *BouncerHandler) URL(pinHTTPS bool, lang, os, product string) (string, error) {
product, err := b.db.AliasFor(product)
if err != nil {
return "", err
Expand Down Expand Up @@ -331,7 +194,7 @@ func (b *BouncerHandler) URL(pinHttps bool, lang, os, product string) (string, e
locationPath = strings.Replace(locationPath, ":lang", lang, -1)

mirrorBaseURL := "http://" + b.PinnedBaseURLHttp
if pinHttps || sslOnly {
if pinHTTPS || sslOnly {
mirrorBaseURL = "https://" + b.PinnedBaseURLHttps
}

Expand All @@ -349,27 +212,27 @@ func (b *BouncerHandler) stubAttributionURL(reqParams *BouncerParams) string {
return b.StubRootURL + "?" + query.Encode()
}

func (b *BouncerHandler) shouldPinHttps(req *http.Request) bool {
if b.PinHttpsHeaderName == "" {
func (b *BouncerHandler) shouldPinHTTPS(req *http.Request) bool {
if b.PinHTTPSHeaderName == "" {
return false
}

return req.Header.Get(b.PinHttpsHeaderName) == "https"
return req.Header.Get(b.PinHTTPSHeaderName) == "https"
}

func fromRTAMO(attribution_code string) bool {
func fromRTAMO(attributionCode string) bool {
// base64 decode the attribution_code value to see if it matches the RTAMO regex
// This uses '.' as padding because Bedrock is using this library to encode the values:
// https://pypi.org/project/querystringsafe-base64/
var base64Decoder = base64.URLEncoding.WithPadding('.')
sDec, err := base64Decoder.DecodeString(attribution_code)
sDec, err := base64Decoder.DecodeString(attributionCode)
if err != nil {
log.Printf("Error decoding %s: %s ", attribution_code, err.Error())
log.Printf("Error decoding %s: %s ", attributionCode, err.Error())
return false
}
q, err := url.ParseQuery(string(sDec))
if err != nil {
log.Printf("Error parsing the attribution_code query parameters: %s", err.Error())
log.Printf("Error parsing the attribution_code query parameter: %s", err.Error())
return false
}

Expand Down Expand Up @@ -439,14 +302,20 @@ func (b *BouncerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}

if reqParams.OS == "" {
reqParams.OS = DefaultOS
reqParams.OS = defaultOS
}

if reqParams.Lang == "" {
reqParams.Lang = DefaultLang
reqParams.Lang = defaultLang
}

// If attribution_code is set, redirect to the stub service.
if b.shouldAttribute(reqParams) {
stubURL := b.stubAttributionURL(reqParams)
http.Redirect(w, req, stubURL, 302)
return
}

isWinXpClient := isWindowsXPUserAgent(req.UserAgent())
isPre2024Stub := isPre2024StubUserAgent(req.UserAgent())
// We want to return ESR115 when... the product is for Firefox
shouldReturnESR115 := strings.HasPrefix(reqParams.Product, "firefox-") &&
// and the product is _not_ an MSI build
Expand All @@ -461,18 +330,6 @@ func (b *BouncerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// and the request doesn't come from mozilla.org
!hasMozorgReferrer(reqParams.Referer)

// If the client is not WinXP and attribution_code is set, redirect to the stub service
if b.shouldAttribute(reqParams) && !isWinXpClient {
stubURL := b.stubAttributionURL(reqParams)
http.Redirect(w, req, stubURL, 302)
return
}

// HACKS
// If the user is coming from windows xp or vista, send a sha1 signed product.
if reqParams.OS == "win" && isWinXpClient {
reqParams.Product = sha1Product(reqParams.Product)
}
// Send the latest compatible ESR product if we detect that this is the best option for the client.
if shouldReturnESR115 {
// Override the OS if we detect a x64 client that attempts to get a stub installer.
Expand All @@ -481,12 +338,13 @@ func (b *BouncerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
reqParams.Product = esr115Product
}

// If the user is an "old" stub installer, send a pre-2024-cert-rotation product.
if isPre2024Stub {
if isPre2024StubUserAgent(req.UserAgent()) {
reqParams.Product = pre2024Product(reqParams.Product)
}

url, err := b.URL(b.shouldPinHttps(req), reqParams.Lang, reqParams.OS, reqParams.Product)
url, err := b.URL(b.shouldPinHTTPS(req), reqParams.Lang, reqParams.OS, reqParams.Product)
if err != nil {
http.Error(w, "Internal Server Error.", http.StatusInternalServerError)
log.Println(err)
Expand Down
Loading

0 comments on commit bda4491

Please sign in to comment.