Skip to content

Commit 954e25b

Browse files
committed
Fix panic for incomplete demos (fixes #3)
Return io.ErrUnexpectedEOF instead
1 parent 80abdf5 commit 954e25b

File tree

3 files changed

+82
-6
lines changed

3 files changed

+82
-6
lines changed

packet_handlers.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ import (
1717
const entitySentinel = 9999
1818

1919
func (p *Parser) handlePacketEntities(pe *msg.CSVCMsg_PacketEntities) {
20+
defer func() {
21+
p.setError(recoverFromPanic(recover()))
22+
}()
23+
2024
r := bit.NewSmallBitReader(bytes.NewReader(pe.EntityData))
2125

2226
currentEntity := -1
@@ -80,13 +84,21 @@ func (p *Parser) readEnterPVS(reader *bit.BitReader, entityID int) *st.Entity {
8084
}
8185

8286
func (p *Parser) handleGameEventList(gel *msg.CSVCMsg_GameEventList) {
87+
defer func() {
88+
p.setError(recoverFromPanic(recover()))
89+
}()
90+
8391
p.gehDescriptors = make(map[int32]*msg.CSVCMsg_GameEventListDescriptorT)
8492
for _, d := range gel.GetDescriptors() {
8593
p.gehDescriptors[d.GetEventid()] = d
8694
}
8795
}
8896

8997
func (p *Parser) handleGameEvent(ge *msg.CSVCMsg_GameEvent) {
98+
defer func() {
99+
p.setError(recoverFromPanic(recover()))
100+
}()
101+
90102
if p.gehDescriptors == nil {
91103
p.warn("Received GameEvent but event descriptors are missing")
92104
return
@@ -517,6 +529,10 @@ func getCommunityID(guid string) int64 {
517529
}
518530

519531
func (p *Parser) handleUpdateStringTable(tab *msg.CSVCMsg_UpdateStringTable) {
532+
defer func() {
533+
p.setError(recoverFromPanic(recover()))
534+
}()
535+
520536
cTab := p.stringTables[tab.TableId]
521537
switch cTab.Name {
522538
case stNameUserInfo:
@@ -527,10 +543,13 @@ func (p *Parser) handleUpdateStringTable(tab *msg.CSVCMsg_UpdateStringTable) {
527543
// Only handle updates for the above types
528544
p.handleCreateStringTable(cTab)
529545
}
530-
531546
}
532547

533548
func (p *Parser) handleCreateStringTable(tab *msg.CSVCMsg_CreateStringTable) {
549+
defer func() {
550+
p.setError(recoverFromPanic(recover()))
551+
}()
552+
534553
if tab.Name == stNameModelPreCache {
535554
for i := len(p.modelPreCache); i < int(tab.MaxEntries); i++ {
536555
p.modelPreCache = append(p.modelPreCache, "")
@@ -617,6 +636,10 @@ func (p *Parser) handleCreateStringTable(tab *msg.CSVCMsg_CreateStringTable) {
617636
}
618637

619638
func (p *Parser) handleUserMessage(um *msg.CSVCMsg_UserMessage) {
639+
defer func() {
640+
p.setError(recoverFromPanic(recover()))
641+
}()
642+
620643
switch msg.ECstrike15UserMessages(um.MsgType) {
621644
case msg.ECstrike15UserMessages_CS_UM_SayText:
622645
st := new(msg.CCSUsrMsg_SayText)
@@ -679,6 +702,10 @@ type frameParsedTokenType struct{}
679702
var frameParsedToken = new(frameParsedTokenType)
680703

681704
func (p *Parser) handleFrameParsed(*frameParsedTokenType) {
705+
defer func() {
706+
p.setError(recoverFromPanic(recover()))
707+
}()
708+
682709
for k, rp := range p.rawPlayers {
683710
if rp == nil {
684711
continue

parser.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"io"
66
"os"
7+
"sync"
78

89
dp "github.com/markus-wa/godispatch"
910

@@ -48,6 +49,8 @@ type Parser struct {
4849
stringTables []*msg.CSVCMsg_CreateStringTable
4950
cancelChan chan struct{}
5051
warn WarnHandler
52+
err error
53+
errLock sync.Mutex
5154
}
5255

5356
// Map returns the map name. E.g. de_dust2 or de_inferno.
@@ -133,6 +136,21 @@ func (p *Parser) TState() *TeamState {
133136
return &p.tState
134137
}
135138

139+
func (p *Parser) error() (err error) {
140+
p.errLock.Lock()
141+
err = p.err
142+
p.errLock.Unlock()
143+
return
144+
}
145+
146+
func (p *Parser) setError(err error) {
147+
if err != nil {
148+
p.errLock.Lock()
149+
p.err = err
150+
p.errLock.Unlock()
151+
}
152+
}
153+
136154
// TeamState contains a team's ID, score, clan name & country flag.
137155
type TeamState struct {
138156
id int

parsing.go

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package demoinfocs
33
import (
44
"errors"
55
"fmt"
6+
"io"
67

78
common "github.com/markus-wa/demoinfocs-golang/common"
89
events "github.com/markus-wa/demoinfocs-golang/events"
@@ -53,7 +54,13 @@ func (p *Parser) ParseHeader() error {
5354
// ParseToEnd attempts to parse the demo until the end.
5455
// Aborts and returns an error if Cancel() is called before the end.
5556
// May panic if the demo is corrupt in some way.
56-
func (p *Parser) ParseToEnd() error {
57+
func (p *Parser) ParseToEnd() (err error) {
58+
defer func() {
59+
if err == nil {
60+
err = recoverFromPanic(recover())
61+
}
62+
}()
63+
5764
if p.header == nil {
5865
panic(msgHeaderNotParsed)
5966
}
@@ -70,12 +77,28 @@ func (p *Parser) ParseToEnd() error {
7077

7178
// Close msgQueue
7279
close(p.msgQueue)
73-
return nil
80+
81+
return p.error()
7482
}
7583
}
84+
85+
if pErr := p.error(); pErr != nil {
86+
return pErr
87+
}
7688
}
7789
}
7890

91+
func recoverFromPanic(r interface{}) error {
92+
if r != nil {
93+
if r == io.ErrUnexpectedEOF {
94+
return io.ErrUnexpectedEOF
95+
} else {
96+
panic(r)
97+
}
98+
}
99+
return nil
100+
}
101+
79102
// Cancel aborts ParseToEnd(). All information that was already read
80103
// up to this point will still be used (and new events may still be sent).
81104
func (p *Parser) Cancel() {
@@ -85,12 +108,18 @@ func (p *Parser) Cancel() {
85108
// ParseNextFrame attempts to parse the next frame / demo-tick (not ingame tick).
86109
// Returns true unless the demo command 'stop' was encountered.
87110
// Panics if header hasn't been parsed yet - see Parser.ParseHeader().
88-
func (p *Parser) ParseNextFrame() bool {
111+
func (p *Parser) ParseNextFrame() (b bool, err error) {
112+
defer func() {
113+
if err == nil {
114+
err = recoverFromPanic(recover())
115+
}
116+
}()
117+
89118
if p.header == nil {
90119
panic(msgHeaderNotParsed)
91120
}
92121

93-
b := p.parseFrame()
122+
b = p.parseFrame()
94123

95124
// Make sure all the messages of the frame are handled
96125
p.msgDispatcher.SyncQueues(p.msgQueue)
@@ -99,7 +128,9 @@ func (p *Parser) ParseNextFrame() bool {
99128
if !b {
100129
close(p.msgQueue)
101130
}
102-
return b
131+
132+
err = p.error()
133+
return
103134
}
104135

105136
func (p *Parser) parseFrame() bool {

0 commit comments

Comments
 (0)